Supajump gives you Organizations → Teams → Users, dynamic RBAC with Row‑Level Security, RSC‑first data fetching, and a batteries‑included DX — all wired up in a Turborepo.
supabase start + pnpm devPowered by your favorite stack
Supajump packages the hard parts so you can focus on your product: auth, permissions, data fetching, and a maintainable file structure.
Organizations → Teams → Users with memberships and role assignments. Clean tenant isolation and sane defaults.
Permission = resource + action + scope (all/own). Enforced via Postgres Row Level Security and helper RPCs.
React Server Components with server‑side prefetch + client hydration using TanStack Query.
Organized by feature folders with colocated components, hooks, queries, and types.
npx/pnpm create to bootstrap a new project with env scaffolding and clear next steps.
Skeleton loaders, react‑hook‑form + zod, and sensible defaults for a production‑ready feel.
/apps/app – Next.js application/packages/create-supajump-app – CLI scaffold/packages/* – shared packages@/*, @features/*env.mjsScaffold a new project with the CLI.
npx @supajump/create-app my-appRun Supabase + the app locally.
cd my-app && supabase start && pnpm devBuild with confidence. RBAC + RLS are already wired in.
pnpm buildapps/app/src/lib/database.types.ts./features/[name]./apps/app/src/queries/keys.ts for proper invalidation.These are the real patterns the template ships with — not hand‑wavy pseudo‑code.
// RSC with server-side prefetch + hydration
import { HydrationBoundary, dehydrate } from "@tanstack/react-query"
import { getQueryClient } from "@/components/providers/get-query-client"
import { api } from "@/queries"
import { postsKeys } from "@/queries/keys"
export default async function Page({ params }) {
const supabase = await createClient()
const queryClient = getQueryClient()
await queryClient.prefetchQuery({
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: postsKeys.list(orgId, teamId),
queryFn: () => api.posts.getAll(supabase, orgId, teamId),
})
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<PostsTable orgId={orgId} teamId={teamId} />
</HydrationBoundary>
)
}-- RLS helper pattern (simplified)
CREATE POLICY "rls_<table>_select" ON <table>
FOR SELECT TO authenticated USING (
supajump.has_permission('<table>', 'view', org_id, team_id, owner_id)
);
CREATE POLICY "rls_<table>_insert" ON <table>
FOR INSERT TO authenticated WITH CHECK (
supajump.has_permission('<table>', 'create', org_id, team_id, owner_id)
);
CREATE POLICY "rls_<table>_update" ON <table>
FOR UPDATE TO authenticated WITH CHECK (
supajump.has_permission('<table>', 'edit', org_id, team_id, owner_id)
);
CREATE POLICY "rls_<table>_delete" ON <table>
FOR DELETE TO authenticated WITH CHECK (
supajump.has_permission('<table>', 'delete', org_id, team_id, owner_id)
);// Client hook
export function usePosts(orgId, teamId) {
const supabase = createClient()
return useQuery({
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: postsKeys.list(orgId, teamId),
queryFn: () => api.posts.getAll(supabase, orgId, teamId),
})
}# Development
supabase start
pnpm dev
# Build / Lint
pnpm build
pnpm lint
# DB management
pnpm db:gen:types
supabase db push
supabase db resetSupajump ships with skeleton components that mirror layouts for instant perceived performance.