Convex SaaS Starter Template
https://www.convex.dev/templates/convex-saas
Features
- 𧩠Convex: A complete, reactive, typesafe backend with authentication and file storage.
- β‘ Vite: Next-Gen Frontend Tooling.
- ποΈ Stripe: Subscription Plans, Customer Portal, and more.
- π Authentication: Email Code and Social Logins.
- π¨ TailwindCSS: Utility-First CSS Framework.
- π ShadCN: Composable React components.
- π Easy Theming: Switch between Light and Dark modes with ease.
- πΊοΈ TanStack Router: Simple Route Definitions.
- π§ Resend: Email for Developers.
- π React Email: Customizable Emails with React.
- π Conform: Type-Safe Form Validation based on Web Fundamentals.
- π₯ File Uploads: Profile Picture Uploads with Convex.
- π I18N: Internationalization for your App.
- π§° TanStack Development Tools: Enhanced Development Experience.
- π
Modern UI: Carefully crafted UI with a Modern Design System.
- π Custom Pages: Landing, Onboarding, Dashboard and Admin Pages.
- π± Responsive: Works on all devices, from Mobile to Desktop.
This was built off of the open source Remix SaaS from Daniel Kanem
19 Replies
dope π₯
Brilliant! Saved my day. Thanks for the ship! ππΎ
It's nice to see the newer eslint file.. i keep wondering if we are crazy for running eslint 9.8 π
. its mostly working... if you don't sneeze
Hey @Wayne , it would be appreciated to have some more elaborate docs on how to set up the stripe dashboard to get everything to work. I assume we have to manually create these plans, but I'm not sure if the nested stripeIds are priceIds and the outer one is a productId?
@David Alonso run the
init
convex action to seed the database and create the entities in Stripe.
@Wayne pull request here: https://github.com/waynesutton/convex-saas/pull/3damn wish I would have known
yesterday I was testing one of the products i manually created and Stripe doesn't let me archive it, is there a way to not make it bail when there's an archived product?
also, I assume this only creates stripe entities in test mode which we cna copy into live mode later?
nvm, found the line and commented it out, why was that there?
It creates entities on whatever stripe account you give it keys for. You can adapt the init script to create your actual products, and give it production stripe credentials when ready.
It's there so the init script doesn't run more than once for a given set of stripe credentials. The starter automatically runs it with the dev command for easy setup.
@erquhart just something to throw in the mix, we're likely going to be introducing some sort of
onLoad
mutation or something that every component (including the app) can register. this mutation would be called every time a deployment happens, or the deployment restarts, etc. it has a chance to "set up" the deployment or the component state or whatever, or run migrations, and so on. this is starting to be a pretty common need.
it can schedule background work or kick off whatever background machinery/workflow should be running inside the deploymentThat will be super helpful
yeah, our team has needed this like a million times
dogfooding ftw
how can I have the starter seed the production database?
cause
convex dev
won't I guess? I wanna be able to run the init script in the prod environmentYou can run the init function like any other convex function, dash or cli
Conditional logic based on env is a code smell but probably the simplest approach for this.
this sounds like a precursor to dynamic table creation. π
I'm confused as to why this shows up like this given the lines of code below:
the intended behavior is to only show paid plans right? Switching to free should be done by cancelling
Is this the UI from stripe or your dashboard
from stripe
Ah, actually, that's a miss from the port
The original starter used the values of those enums, eg., "free", as the product id in stripe, because you can provide your own id for those (but not subscriptions or prices). For consistency and durability I decoupled the stripe id from the plan name, hence
plan.key
in the convex schema.
All that to say, the id will never equal PLANS.FREE
, need to compare to the actual id of the free plan in that filter.Code change if you're not past rerunning the init script: https://github.com/waynesutton/convex-saas/pull/4/files