Giter Club home page Giter Club logo

criticalmoments / cmsaasstarter Goto Github PK

View Code? Open in Web Editor NEW
699.0 10.0 217.0 474 KB

A modern SaaS template/boilerplate built with SvelteKit, Tailwind, and Supabase. Includes marketing page, blog, subscriptions, auth, user dashboard, user settings, pricing page, and more.

Home Page: https://saasstarter.work

License: MIT License

JavaScript 2.48% HTML 0.34% Svelte 71.41% CSS 0.30% PLpgSQL 1.95% TypeScript 23.52%
cloudflare saas saas-boilerplate saas-tools supabase svelte sveltekit daisyui tailwind tailwind-css tailwindcss stripe stripe-checkout postgresql saas-template boilerplate saas-application saas-kit starter-template

cmsaasstarter's Introduction

SaaS Starter

Build Format Check Linting License

SaaS Starter is an open source, fast, and free to host SaaS template / boilerplate

  • Feature Rich: user auth, user dashboard, marketing site, blog engine, billing/subscriptions, pricing page, and more.
  • Lightning Performance: fast pre-rendered pages which score 100/100 on Google PageSpeed.
  • Delighful Developer Experience: tools you'll love working with, including SvelteKit, Tailwind, DaisyUI, Postgres, and Supabase.
  • Extensible: all the tools you need to make additional marketing pages, UI components, user dashboards, admin portals, database backends, API endpoints, and more.
  • Hosting: Our suggested hosting stack is free to host, cheap to scale, easy to manage, and includes automatic deployments.
  • MIT Open Source
  • Fully Functional Demo
  • Quick Start: Full docs from git clone to deployment.

Created by the folks at Critical Moments! Check out our website site for an example depolyment of SaaS Starter.

Make mobile apps? Improve conversion rates and ratings with Critical Moments.

Demo

You can explore all the features using our fully functional demo saasstarter.work.

Try it Now

See criticalmoments.io for an example of what’s possible after this template has design, content, and functionality added.

homepage
pricing page settings page payments portal

Features

Everything you need to get started for a SaaS company:

  • User Authentication: Sign up, sign out, forgot password, email verification, and oAuth. Powered by Supabase Auth.
  • Marketing Page with SEO optimization
  • Blog engine with rich formatting, RSS and SEO optimization.
  • User Dashboard with user profile, user settings, update email/password, billing, and more.
  • Subscriptions powered by Stripe Checkout
  • Pricing page
  • Contact-us form
  • Billing portal: self serve to change card, upgrade, cancel, or download receipts
  • Onboarding flow after signup: collect user data, and select a payment plan
  • Style toolkit: theming and UI components
  • Responsive: designed for mobile and desktop.
  • Extensible: all the tools you need to make additional marketing pages, UI components, admin portals, database backends, API endpoints, and more.

Introduction Blog Post

What to learn why we picked the technologies we did, and how to keep your fork lightning fast as you add content? Check out our blog post on the process. Yes, it's hosted on a SaaS Starter fork!

Tech Stack

  • Web Framework: SvelteKit
  • CSS / Styling
    • Framework: TailwindCSS
    • Component library: DaisyUI
  • Suggested Hosting Stack
    • Host + CDN: Cloudflare Pages
    • Serverless compute: Cloudflare Workers
    • Authentication: Supabase Auth
    • Database: Supabase Postgres
  • Payments
    • Stripe Checkout
    • Stripe Portal

Suggested Hosting Stack

There’s no cost for using this template. The costs below reflect our suggested hosting stack.

  • $0/mo — Supabase free tier, Cloudflare free tier.
    • Pros:
      • Free!
      • Can scale to thousands of users.
      • Unlimited static page requests.
      • 100k serverless functions/day.
    • Cons:
      • Does not include database backups. The frugal among you could hook up pgdump backups on lambda/S3 for a few cents per month.
      • Will auto-pause your database when not in use for 7 days.
    • Who it’s for:
      • This tier is perfectly functional for a hobby project, or pre-revenue company (up to 50,000 monthly active users). It’s easy to scale up once revenue starts, but it’s also fine to keep at this scale indefinitely.
  • $30/mo - Supabase Pro, Cloudfare Workers Paid
    • Pros:
      • Database backups.
      • Never pauses database.
      • Over 1M serverless functions per day, with linear pricing for additional invocations.
    • Cons:
      • none
    • Who it’s for:
      • I suggest moving to this once you have paid customers or investors.

Performance / Best Practices

The selected tech stack creates lightning fast websites.

  • Pre-rendering (static generation) for marketing pages, pricing and blog
  • Instant navigation: the best of CSR + SSR in one. SSR your first page for fastest possible initial load times. For subsequent pages, the content is pre-loaded and rendered with CSR, for instant rendering.
  • CDN optimized, for high edge-cache hit ratios
  • Edge-functions for dynamic APIs/pages
  • Svelte and Tailwind compile out unused HTML, CSS and JS at deploy time for smaller pages
  • Linting to find accessibility and syntax issues

The result is a perfect Google PageSpeed Insights score in all categories!

Screenshot 2024-01-18 at 11 31 32 AM

Quick Start

Get Started (Local Development)

To get started, fork and run this project!

## First fork the project on Github
git pull [Your Fork]
cd CMSaasStarter ## or your fork name if different
npm install
## Create an env file. You'll replace the values in this in later steps.
cp local_env_template .env.local
## Run the project locally in dev mode, and launch the browser
npm run dev -- --open

Developer Environment

The repo includes CI scripts designed for GitHub Actions. These confirm you don’t break your build, you use proper code formatting, and code linting and typechecking passes. Github disables CI on forks by default, so if you fork this repo, be sure to go into the Github Actions page for your repo and enable workflows.

You can manually run these scripts yourself; npm run build for the build, npm run format_check to check formatting, npm run lint for the linting, npm run check for typechecking, and npm run test for testing (if you add tests).

Installing extensions in your editor can automatically format-on-save, show linting/type issues inline, and run your test cases:

To catch build, formatting, linting and test issues before you commit changes, we suggest the following local git hook. It will run before you commit, stop you from breaking the build, and show any issues that are found. Add the lines below to an executable git hook script at the location .git/hooks/pre-commit.

#!/bin/sh
set -e
npm run format_check
npm run lint
npm run build
npm run check
npm run test_run

Finally: if you find build, formatting or linting rules too tedious, you can disable enforcement by deleting the CI files (.github/workflows/*) and remove the git hook (.git/hooks/pre-commit).

Setup Supabase

  • Create a Supabase account
  • Create a new Supabase project in the console
  • Wait for the database to launch
  • Create your user management tables in the database
    • Go to the SQL Editor page in the Dashboard.
    • Paste the SQL from database_migration.sql in this repo to create your user/profiles table and click run.
  • Enable user signups in the Supabase console: sometimes new signups are disabled by default in Supabase projects
  • Go to the API Settings page in the Dashboard. Find your Project-URL (PUBLIC_SUPABASE_URL), anon (PUBLIC_SUPABASE_ANON_KEY) and service_role (PRIVATE_SUPABASE_SERVICE_ROLE).
    • For local development: create a .env.local file:
      PUBLIC_SUPABASE_URL=https://your-project.supabase.co
      PUBLIC_SUPABASE_ANON_KEY=your-anon-key
      PRIVATE_SUPABASE_SERVICE_ROLE=your service_role secret
      
    • For production, add these two keys to your Cloudflare environment (see below). We suggest you encrypt your service role.
  • Auth Callback
    • Set your default callback URL for auth in the Supabase Auth console. For example, for the demo page we added: https://saasstarter.work/auth/callback . Also add that same URL to the the “allowed redirect URL” list in the Supabase auth console further down the page.
    • Add a link to the redirect URL allow list which allows parameters to your auth callback. For example we added the following for the demo page: https://saasstarter.work/auth/callback?*
    • Also add any local development URLs you want to use in testing to the list for your dev environment. For example, we added the following for local development: http://localhost:5173/auth/callback and http://localhost:5173/auth/callback?*.
    • Test that the "sign up" and "forgot password" emails link back to your domain correctly by checking the have a redirect_to parameter to your yourdomain.com/auth/callback. If they link to the base URL or another page, double check you have the config above set correctly.
  • OAuth Logins
    • Decide which oauth logins you want to support, and set them up in the Supabase Auth console under “Auth Providers”. Be sure to provide them the Supabase callback URL. Also be sure to set any platform specific permissions/settings to retrieve their email as part of the login (for example, for Github it's under Account Permissions > Email Address > Read Only Access
    • Edit oauthProviders list in /src/routes/(marketing)/login/login_conf.ts with the list of providers you chose. If you don’t want any OAuth options, make this an empty array.
    • Test each provider to ensure you setup the client ID, client secret and callback correctly for each
  • Auth Email SMTP
    • Supabase has a limit of 4 emails per hour on their development server. You should Configure a Custom SMTP sending emails from your own domain.
    • Customize the email templates in the Supabase Auth console to include your product name and branding
  • Test authentication
    • Open the /login page in your browser, and ensure you can sign up, confirm email, log in, and edit your account.

Setup Stripe Billing

  • Create a Stripe account
  • Create a product and price Tiers
    • Create your products and their prices in the Dashboard or with the Stripe CLI.
    • SaaS Starter works best if you define each tier as a separate product (eg, SaaS Starter Free, Saas Starter Pro, Saas Starter Enterprise). Include a monthly and annual price for each product if you want to support multiple billing periods.
    • You do not need to create a free plan in Stripe. The free plan is managed within the app.
  • Setup your environment
    • Get your Secret API key, and add it as an environment variable PRIVATE_STRIPE_API_KEY (.env.local locally, and Cloudflare environment for prod). Be sure to use test keys for development, and keep your production/live keys secret and secure.
  • Optional: theme your Stripe integration
    • Change the colors and fonts to match your brand here
  • Update your pricing plan data to align to your stripe data
    • See /src/routes/(marketing)/pricing/pricing.js and Fill in all fields for each plan. stripe_price_id and stripe_product_id should only be omitted on a single “free” plan. Multiple free plans are not supported.
      • The product in Stripe can contain several prices for the same product (annual, monthly, etc). The stripe_price_id you choose to put in this json will be the default we use for the checkout experience. However, if you have more prices configured for a product configured, the user can switch between them in the management portal.
    • Set the defaultPlanId to the plan the user will see as their “current plan” after signup, but before subscribing to a paid plan (typically “free”). It should align to the plan with no stripe_price_id.
    • if you want an item highlighted on /pricing, specify that plan ID in /src/routes/(marketing)/pricing/+page.svelte
  • Update your portal configuration
    • Open stripe portal config and make the following changes
      • Disallow editing email under customer information (since we allow editing in primary portal)
      • Optional: setup a custom domain so Stripe pages use your own domain
  • Repeat steps in production environment

Deploy To Cloudflare

Cloudflare Pages and Workers is one of the most popular options for deploying SvelteKit and we recommend it. Follow Cloudflare’s instructions to deploy in a few clicks. Be sure to select “SvelteKit” as framework, and the rest of the defaults will work.

When prompted: add environment variables for your production environment (PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, PRIVATE_SUPABASE_SERVICE_ROLE, and PRIVATE_STRIPE_API_KEY).

Optional: enable Cloudflare Analytics for usage metrics.

If you prefer another host you can explore alternatives:

Add your content!

After the steps above, you’ll have a working version like the demo page. However, it’s not branded, and doesn’t have your content. The following checklist helps you customize the template to make a SaaS homepage for your company.

  • Set a name for your site in src/config.ts:WebsiteName
  • Content
    • Add actual content for marketing homepage
    • Add actual content for your blog (or delete the blog)
      • Update all fields in src/routes/(marketing)/blog/posts.ts, and replace the post pages under src/routes/(marketing)/blog/posts to align to the urls from posts.ts.
      • Alternatively remove the blog by removing the src/routes/(marketing)/blog directory, and remove any links to the blog in the header and footer. You can always bring it back later.
    • Add any pages you want on top of our boiler plate (about, terms of service, etc). Be sure to add links to them in the header, mobile menu header, and footer as appropriate (src/routes/(marketing)/+layout.svelte).
    • Note: if you add any dynamic content to the main marketing page, pricing page or blog, be sure to set prerender = false in the appropriate +page.ts file. These are currently pre-rendered and served as static assets for performance reasons, but that will break if you add server side rendering requirements.
  • Update SEO content
    • Update title and meta description tags for every public page. We include generic ones using your site name (src/config.ts:WebsiteName), but the more specific these are the better.
    • This done automatically for blog posts from posts.ts metadata
  • Style
    • Create a new DaisyUI Theme matching your brand or use one of the built in themes from DaisyUI (see tailwind.config.js)
    • Update the marketing page layout src/routes/(marketing)/+layout.svelte: customize design, delete unwanted pages from header and footer
    • Style: make it your own look and feel.
    • Update the favicon in the /static/ directory
    • The Authentication UI should automatically update based on your DaisyUI style, but check out the login in pages, and further design tweaks can be made in src/routes/(marketing)/login/login_config.ts (see Auth UI for options).
  • Functionality
    • Add actual SaaS functionality!
    • Replace the admin dashboard with real content (/src/routes/(admin)/account/+page.svelte).
    • Add API endpoints and database tables as needed to deliver your SaaS product.

Extensions

The open source community is extending and improving SaasStarter!

These extensions are reference implementations of commonly needed features. We don't integrate them into the main branch to keep our dependencies minimal and simplify maintenance. However, if you need them you can cherry pick into your fork:

Icons

Homescreen Icons are from Solar Broken Line Icons and Solar Linear Icons via CC Attribution License.

Sponsor

We hope you enjoy SaaS Starter! If you build mobile apps, please check out its sponsor/creator, Critical Moments. We can help improve your mobile app conversions, improve your app rating, and mitigate major bugs and outages.

cmsaasstarter's People

Contributors

charlbest avatar charlbest-toast avatar dependabot[bot] avatar keybits avatar mgenovski avatar owenversteeg avatar scosman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cmsaasstarter's Issues

Internal error viewing home and pricing page

Thanks for your work on this great project.

I've cloned the repo and configured it as per the README. I can create a user account and can see pages at:

http://localhost:5173/blog
http://localhost:5173/contact_us
http://localhost:5173/login
and
http://localhost:5173/account

^ all work correctly

However when I navigate to the home or pricing pages (http://localhost:5173/ or http://localhost:5173/pricing) I get:

image

"This is embarassing...
There was an error: Internal Error"

In the console I see:

image
The next HMR update will cause the page to reload
<Root> was created without expected prop 'form'

I haven't made any other changes. Tested with node 18.6.0 and 20.11.1

Any thoughts on what's causing this?

Change Password Bug

When signing up in the demo with an email the change password feature does not seem to work. Based on the feedback given by the site it wrongly identified my account as an OAuth account and thus didn't allow changing the password. Using the forgot password feature did work.

Stripe webhook missing?

I can't seem to understand how stripe updates the database when there are billing errors. Can you please help me understand.?

SvelteKit 2 / Svelte 5 Upgrade

We should probably do these at the same time. We can keep in a branch until Svelte 5 is released, but some folks will want to start on that branch.

Open to help from any Svelte enthusiasts who want to tackle this.

Move site name into code

Several folks have gotten tripped up on the env var approach. Will move into code with a default that’s easy to change instead.

Clicking Sign Up sends multiple emails

Clicking the sign up button will send multiple requests/emails. This is probably the case for "forgot password" as well.

image

Possible solutions:

  • disable the button after you click it with a 1 minute re-activation
  • indefinitely disable button (until refresh)
  • button is still clickable but in the background, debounce and don't send another request within x seconds

Make Login/Signup/Forgot-Password Pages "Sveltey"

The login pages use @supabase/auth-ui-svelte but despite the name, these are wrappers of a general javascript framework, that doesn't produce Svelte. They can't be pre-rendered, are a bit slower to load, and show a visual stutter when applying style. I also don't love the error message handling (shown below, not inline), and the "in progress" indicator (too subtle, and can be below the viewport on mobile).

We can re-implement these in pure svelte for pre-render/consistency/speed, and fix the UX issues mentioned above.

P2: everything is functional and works fine as is.

Open for community input on this one.

'relation "public.stripe_customers" does not exist'

Hi there! I'm failing to set up billing pages. After the initial set up of stripe and supabase, I am not able to open the billing page and apply for subscription page. When I add further error debug statements, I came to this error: relation "public.stripe_customers" does not exist. Does anybody know a solution or what I might be doing wrong?
I am using test environment in Stripe for the moment if that matters.
Thank you!

Some production things

Thank you for the lovely template. Currently using it for a (currently) closed-source app, but wanted to share some things we ran into while adjusting this for our production use-case.

  • Resetting the password broke at some point as the amr token didn't seem to be set.
  • Supabase auth has some updates that spam warnings when using the getsession method on the server side like here. Should be fixed in a new version now.
  • Discounts. Applying discounts is a common thing but not trivial with the current pricing scheme system (as far as we could see)
  • Company name and website name should be optional when creating an account
  • We switched most forms to using superforms to make validation a bit clearer on the DX side (particularly for optional elements)

Happy to expand on things if requested. We're still in the process of smoothing out some bugs so maybe I'll add some more later.

Idea: releases, changesets and upgrade paths

So let's say Bob uses the starter and makes a site and then a month later Alice fixes a bug, or adds a feature, or some other change happens here in this repo. At that point, Bob probably has heavily modified the starter and he can abandon all hope of a clean Git add upstream/fetch/merge, he's probably going to have to manually pick stuff over by hand.

Of course some (many) of those things may not be things Bob wants or needs, so my thought is that it's probably best to have changes broken out individually. Commit-level is too granular and a pain, release-level is too broad, so I propose that each PR adds a simple one-line-of-text changeset that starts with (feature/bugfix/SECURITY/template) and a Github compare link with hashes in URL. Major project restructuring or extreme changes should be a new major release. As part of this, it'd be important that PRs with an important feature or bugfix come in one piece. Five (or ten, or thirty) commits in one PR is fine, but as soon as there are multiple PRs to fix one bug then things get a bit out of hand. This is good practice for other reasons though, so shouldn't be an issue.

I imagine most changes will be template, aka not important to merge for people that use the starter. Here's how I'd see this going:

(Date merged into main) (Type) (Change text) (Compare link)
March 15 2024 - Template - change wording on blog - [changes](https://github.com/CriticalMoments/CMSaasStarter/compare/ffd1a1d...5e831e1)
March 12 2024 - Template - modify homepage colors - [changes]
March 9 2024 - SECURITY - prevent API key leakage - [changes]
March 8 2024 - Template - increase footer padding - [changes]
March 4 2024 - Bugfix - prevent menu collapse on iOS 14 - [changes]

Probably the easiest is just a CHANGES.md that people manually update on each PR? There's this - https://github.com/apps/changeset-bot - but that's more complex and for monorepos.

Thoughts? This is probably the easiest way to let people "get" security and/or feature updates while not making things too complex.

"New" Supabase auth ssr package

Amazing project! Exactly what I was looking for.

Curious what are your thoughts on the @supabase/ssr package rather than using auth-helpers?
It seems going forward that it is the package that will get the updates and be maintained. That said, I'm not sure if there are differences now so wouldn't have an influence at this stage, right?

image

Saw a YouTuber do the migration, if you are interested.

Issue with Profile Creation: Cross-site POST Form Submission Error

Hello,

Firstly, I'd like to thank you for making this project available to the community; it's greatly appreciated.

I've been eager to try it out, but I've encountered an issue during the profile creation phase. After successfully logging in, attempting to create my profile results in the following error:

"message": "Cross-site POST form submissions are forbidden"

Additionally, I observed this response from the server:

Failed to load resource: the server responded with a status of 403 () https://my-url.com/account/api?/updateProfile

I'm wondering if this might be related to a missing or incorrect environment variable configuration, particularly regarding the URL setup. Could you provide some guidance on this issue? If there's any additional information or logs that would be helpful, please let me know, and I'll provide them as soon as possible.

Thanks in advance for your time and assistance!

Best regards,

General improvements. Thoughts...

I'm planning on using this starter for one of my projects and there are some things I want to add. Curious to hear if I should encapsulate some of them, that you would be interested in and make a PR(s). Before I do unnecessary work that you don't think fits into this project, here is a rough list I was thinking about.

Daisy UI:

  • Cookies prompt (toast comp + alert comp + local storage to remember dismissal) eg.
  • Dark mode (local storage or save theme against logged in user + html attribute for daisyui <html data-theme="dark")

Supabase:

  • Image uploader (basic type input field. free supabase is only 1GB so upload limit should be extremely small and maybe postgres cron job to clear storage once a week)
  • Leaked Password Protection (recently released and provides error response that can show custom UI)
  • Full text search (postgress tsvector extension)
  • Two factor auth (mfa implementation + account linking)

JS/HTML:

  • Native share actions (Web share api navigator.share())
  • Content Security Policy (meta tag + local/prod <meta http-equiv="Content-Security-Policy")

Additional:

Consider addding basic CRUD functionality

First off congrats for this amazing starter kit. ! Basic CRUD funcionality (maybe a simple todo / notes list), would help less experienced devs and would demostrate the right practices for db operations, loading states and even validation. File uploads would also be a great addition.
Again, I for one am grateful for your contribution to the wonderful sveltekit community.

Managing Subscription Throws a PSE Error

When logged in and clicking on "Manage Subscription" the user gets thrown an error page instead of going to the Stripe page.

Console log shows:

Preloading data for /account/billing/manage failed with the following error: Unknown error (PSE). If issue persists, please contact us. If this error is transient, you can ignore it. Otherwise, consider disabling preloading for this route. This route was preloaded due to a data-sveltekit-preload-data attribute. See https://kit.svelte.dev/docs/link-options for more info

and

GET http://localhost:5173/account/billing/manage 500 (Internal Server Error)

Screenshot:

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.