⚠️ Disclaimer: This is an educational project built for learning purposes only. "Vector" is a fictional name used for this demo — we do not claim any trademark or intellectual property rights over it. This project is not affiliated with, endorsed by, or connected to Linear, Jira, Asana, or any other project management platform. All organization names, issues, and user data in the seed files are entirely fictional. Third-party service names (Clerk, Convex, Vercel, Next.js, OpenAI, Tailwind CSS, etc.) are trademarks of their respective owners and are used here solely to describe the technologies used in this project.
A full-stack, real-time Linear clone — a B2B multi-tenant SaaS issue tracker where teams manage issues on Kanban boards, plan projects and cycles, collaborate with comments and mentions, and ship faster with a built-in AI agent that knows their entire workspace.
Who is this for? Anyone who wants to learn how to build a production-grade, multi-tenant B2B SaaS using modern tools — or anyone looking for a serious starter template for their own project management product.
What makes it different? Every update is real-time (no page refreshes). Auth, organizations, AND billing are handled by Clerk — no Stripe wiring required. The backend is powered by Convex — a reactive database that pushes changes to every connected client instantly. And there's a fully working AI agent (Convex Agent + OpenAI) with org-scoped tools, triage assist, duplicate detection, and rate limiting.
Under the hood Next.js 16 App Router · Convex reactive backend · Clerk auth + organizations + B2B billing · Convex Agent + OpenAI · @dnd-kit drag & drop · shadcn/ui · Tailwind CSS v4 · TypeScript strict mode
You'll need free accounts on these services to run the app. Set them up before cloning:
| Service | What it does | Sign up |
|---|---|---|
| Clerk | Authentication, organizations, and B2B subscription billing | Create a free Clerk account → |
| Convex | Real-time backend, database, vector search, and file storage | Create a free Convex account → |
| OpenAI | Powers the AI agent, triage assist, and duplicate detection | platform.openai.com → |
| Vercel (optional) | Deployment & hosting | vercel.com → |
Think of Vector as your own mini Linear — a modern issue tracker built from scratch as a learning project, with the same keyboard-first, dense, dark-mode-default aesthetic.
It's a multi-tenant workspace app. Every organization gets its own isolated workspace, and members collaborate inside it in real time:
As a team member, you can:
- Create and manage issues with statuses, priorities, assignees, estimates, due dates, and labels
- Drag issues across a Kanban board that updates for everyone instantly
- Search the entire workspace with full-text search and save filtered views
- Comment on issues with @mentions, attach files, link related issues, and break work into sub-issues
- See who else is viewing an issue with live presence
- Fly through everything with a command palette (⌘K) and single-key shortcuts
As a workspace admin, you can:
- Organize work into teams (each with its own issue prefix like
ENG-123), projects, and cycles (sprints) - Invite members and manage roles
- Upgrade the workspace and manage billing — checkout, plan changes, and invoices all handled by Clerk
- Unlock the AI agent on paid plans
The AI agent (Pro/Enterprise) can:
- Create, update, and search issues for you using natural language
- Summarize cycles, report project status, and generate standups
- Detect duplicate issues using vector embeddings
- Triage new issues automatically
Popular use cases:
- 🎓 Portfolio project — show off a real multi-tenant B2B SaaS to employers
- 🚀 SaaS starter — fork it and turn it into your own productivity tool
- 📚 Learn the modern stack — see exactly how Convex, Clerk billing, and AI agents fit together
Want to build apps like this from scratch? Learn how to code with AI the right way — using Cursor and AI agents as force multipliers, not crutches.
- ⚡ Next.js 16 — App Router, Server Components, route groups, and
proxy.tsmiddleware - 🔐 Clerk — Authentication, organizations, role-based access, and B2B subscription billing
- 🗄️ Convex — Real-time reactive backend, vector search, file storage, and schema design
- 🤖 AI-Powered Development — Learn to code with AI the right way: plan, parallelize, review, and ship with Cursor instead of blindly accepting output
- 🎨 Modern UI — shadcn/ui, Tailwind CSS v4, dark mode, and keyboard-first UX patterns
- 💬 Join thousands of developers building together
- 🏆 Real results from graduates who landed jobs and launched products
- 📦 Full course materials, starter code, and lifetime access
- Step-by-step video walkthroughs of real-world builds
- Private community access
- Code reviews and Q&A support
- Regular new project drops
👉 Join the PAPAFAM and start building →
- 📝 Full issue tracking — Statuses (Backlog → Todo → In Progress → In Review → Done / Canceled), five priority levels, assignees, estimates, due dates, and color-coded labels
- 🔢 Team-scoped issue keys — Every team gets a prefix, so issues read like
ENG-42orDESIGN-7 - 🗂️ Kanban board — Drag & drop with @dnd-kit and fractional sort ordering; every move syncs to all clients instantly
- 🔍 Full-text search — Convex search indexes across issue titles and descriptions, scoped to your workspace
- 💾 Saved views — Save filter combinations as personal or shared views
- ⌨️ Command palette — ⌘K for everything, plus single-key shortcuts (Linear style)
- 💬 Comments with @mentions — Mention teammates by name in issue discussions
- 📜 Activity feed — Every status change, assignment, and label is logged and rendered on the issue
- 🌳 Sub-issues & relations — Break work down with parent/child issues; link issues as blocks, blocked by, related, or duplicate of
- 📎 Attachments — Upload files to issues using Convex file storage
- 🟢 Live presence — See who's viewing the same issue in real time
- 📊 Projects — Group issues across teams with statuses, leads, target dates, and live progress (issue counts by status)
- 🔄 Cycles — Time-boxed sprints per team, auto-numbered, with current-cycle tracking
- 🏗️ Multi-team workspaces — Unlimited teams per organization, each with its own board and cycles
- 🤖 Workspace-aware chat — A Convex Agent + OpenAI assistant with org-scoped tools: create/update/search issues, summarize cycles, report project status, list members
- 🧠 Duplicate detection — Issues are embedded (1536-dim vectors) on create; the agent finds semantically similar issues before you file the same bug twice
- 🩺 Triage assist — AI-suggested priority and labels for new issues
- 📋 Standup & cycle reports — Generate summaries of what shipped and what's blocked
- ⏱️ Rate limiting — 50 messages/user/day on Pro, unlimited on Enterprise (
@convex-dev/rate-limiter)
- 🏢 Clerk Organizations — Every workspace is a Clerk organization; memberships and roles sync to Convex via webhooks
- 💳 Clerk B2B Billing — Custom-designed pricing page with checkout, plan management, and invoices handled entirely by Clerk (no Stripe integration to write)
- 🔐 Plan gating, done right — UI gates with
has({ plan })/has({ feature })are cosmetic; Convex mutations are the real enforcement - 🚦 Free-tier limits — Seats, projects, and issue caps enforced server-side with friendly upgrade prompts in the UI
| Free | Pro ($20/mo) | Enterprise ($99/mo) | |
|---|---|---|---|
| Members | 3 | 10 (seat-based, +$10/seat after first) | Unlimited |
| Projects | 2 | Unlimited | Unlimited |
| Issues | 100 | Unlimited | Unlimited |
| AI agent | — | ✅ 50 msgs/user/day | ✅ Unlimited |
| Priority support | — | — | ✅ |
Annual billing: Pro $16/mo-equivalent, Enterprise $79/mo-equivalent.
- ⚛️ Next.js 16 App Router — Route groups for marketing vs. app shell, dynamic
[orgSlug]workspace routes,proxy.tsmiddleware - 🔄 Convex reactive backend — No polling, no refetching. Queries are live subscriptions
- 🛡️ Custom function wrappers —
orgQuery/orgMutationresolve the user, org, and membership from the Clerk JWT and enforce org scoping on every single function (Convex's answer to RLS) - 🔐 Clerk webhooks → Convex — Users, organizations, memberships, and subscriptions sync via Svix-verified webhooks; Clerk stays the source of truth
- 🧩 Convex components —
@convex-dev/agent(AI),@convex-dev/presence(live presence),@convex-dev/rate-limiter(AI quotas) - 🔎 Search + vector indexes — Full-text search indexes and a 1536-dimension vector index, both defined in the schema
- 🖱️ @dnd-kit drag & drop — With fractional
sortOrderranking so reorders are O(1) writes - 🧩 shadcn/ui + Tailwind CSS v4 — Linear-style density, dark theme by default, light mode via
next-themes - ✅ Validators everywhere — Every public Convex function validates args AND return values
flowchart LR
SignUp[Sign Up] --> Onboard[Create / Join Org]
Onboard --> Team[Pick a Team]
Team --> Create[Create Issues]
Create --> Board[Drag on Board]
Board --> Collab[Comment & Mention]
Collab --> Ship[Done 🎉]
flowchart LR
CreateOrg[Create Org] --> Invite[Invite Members]
Invite --> Limits[Hit Free Limits]
Limits --> Upgrade[Upgrade via Clerk Checkout]
Upgrade --> Webhook[Subscription Webhook → Convex]
Webhook --> AI[AI Agent Unlocked]
flowchart TB
Browser[Next.js Frontend] -->|"useQuery / useMutation"| Convex[Convex Backend]
Convex -->|"Real-time sync"| Browser
Clerk[Clerk Auth + Orgs + Billing] -->|"Svix Webhooks"| ConvexHTTP["Convex HTTP /clerk-webhook"]
ConvexHTTP -->|"Sync users, orgs, members, subscriptions"| Convex
Convex -->|"Agent tools + embeddings"| OpenAI[OpenAI]
Convex -->|"File storage"| Storage[Convex Storage]
Browser -->|"proxy.ts middleware"| Clerk
- Node.js 18 or later
- pnpm (package manager) —
npm install -g pnpm - A Clerk account
- A Convex account
- An OpenAI API key (for the AI agent)
git clone https://github.com/sonnysangha/linear-clone-clerk-nextjs-16.git
cd linear-clone-clerk-nextjs-16pnpm installCreate a .env.local file in the project root:
# Clerk — https://go.clerk.com/Mzf8gfM
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-instance.clerk.accounts.dev
# Clerk routing
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/onboarding
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/onboarding
# Convex — https://convex.dev/referral/SONNYS4371
CONVEX_DEPLOYMENT=dev:your-deployment
NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
NEXT_PUBLIC_CONVEX_SITE_URL=https://your-deployment.convex.site🔒 Security: Never commit
.env.localto git. TheNEXT_PUBLIC_prefix means the value is exposed to the browser — only use it for public keys likeNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY.
- Go to your Clerk Dashboard and create a new application
- Copy your Publishable Key, Secret Key, and Frontend API URL into
.env.local - Enable Organizations (Configure → Organizations)
- Create a JWT template named exactly
convex, and add these custom claims so Convex knows the active organization:
{
"org_id": "{{org.id}}",
"org_slug": "{{org.slug}}",
"org_role": "{{org.role}}"
}- Set up Billing (Configure → Billing) with three plans for organizations:
free_org— Freepro— $20/mo base + $10/seat after the first (annual: $16/mo-equivalent), max 10 membersenterprise— $99/mo flat (annual: $79/mo-equivalent)
- Attach features to the paid plans:
ai_agent,unlimited_projects,unlimited_issues,unlimited_seats,unlimited_ai,priority_support - Copy your plan IDs into
lib/plans.ts(they're referenced there, never inline in components)
- Run
npx convex dev— this will prompt you to create a new project or link an existing one, and fills inCONVEX_DEPLOYMENT/NEXT_PUBLIC_CONVEX_URLfor you - Set these environment variables in the Convex dashboard (Settings → Environment Variables) — or via the CLI:
npx convex env set CLERK_FRONTEND_API_URL https://your-instance.clerk.accounts.dev
npx convex env set CLERK_WEBHOOK_SECRET whsec_... # from step 6 below
npx convex env set OPENAI_API_KEY sk-... # for the AI agentThis is how Clerk keeps your Convex database in sync with users, organizations, memberships, and subscriptions.
- In your Clerk dashboard, go to Webhooks and create a new endpoint
- Set the URL to your Convex HTTP Actions URL +
/clerk-webhook:- e.g.
https://your-deployment.convex.site/clerk-webhook(note:.convex.site, not.convex.cloud)
- e.g.
- Subscribe to these events:
user.created/user.updated/user.deletedorganization.created/organization.updated/organization.deletedorganizationMembership.created/organizationMembership.updated/organizationMembership.deleted- All
subscription.*andsubscriptionItem.*events (this is how plan changes reach Convex)
- Copy the Signing Secret and set it as
CLERK_WEBHOOK_SECRETin your Convex environment variables (step 5)
pnpm devThis runs the Next.js frontend and the Convex backend in parallel. Open http://localhost:3000, sign up, create an organization on the onboarding screen, and you're in.
- Clerk account created and keys added to
.env.local - Clerk Organizations enabled
- Clerk JWT template
convexcreated withorg_id/org_slug/org_roleclaims - Clerk Billing plans (
free_org,pro,enterprise) and features configured - Convex project linked;
CLERK_FRONTEND_API_URL,CLERK_WEBHOOK_SECRET, andOPENAI_API_KEYset in Convex env vars - Clerk webhook pointing at
https://<deployment>.convex.site/clerk-webhook -
pnpm devruns without errors - You can sign up, create an org, and create your first issue
Vector uses Convex as its database with a flat, relational schema design. All tables are defined in convex/schema.ts.
| Table | Purpose | Key Fields |
|---|---|---|
| users | Synced from Clerk via webhooks | clerkId, name, email, imageUrl |
| organizations | Synced from Clerk Organizations | clerkOrgId, slug, plan, subscriptionStatus |
| members | Org membership with roles | orgId, userId, role, clerkMembershipId |
| teams | Teams within an org | orgId, name, key (issue prefix), nextIssueNumber |
| issues | The core entity | teamId, number, status, priority, assigneeId, sortOrder, embedding |
| labels / issueLabels | Color-coded labels (many-to-many) | name, color / issueId, labelId |
| issueRelations | Links between issues | issueId, relatedIssueId, type (blocks, related, duplicate…) |
| comments | Issue discussions | issueId, authorId, body, mentions[] |
| activity | Audit trail per issue | issueId, actorId, type, oldValue, newValue |
| projects | Cross-team initiatives | orgId, status, leadId, targetDate |
| cycles | Sprints per team | teamId, number, startDate, endDate |
| attachments | Files on issues (Convex storage) | issueId, storageId, fileName, fileSize |
| views | Saved filter configurations | creatorId, filters (JSON), shared |
- Flat, relational design — No deep nesting. Tables reference each other by ID, following Convex best practices.
- Clerk is the source of truth —
users,organizations, andmembersare only ever written by webhooks. Feature code never touches them. - Org scoping everywhere — Almost every table carries
orgId, and every Convex function runs throughorgQuery/orgMutationwrappers that verify membership before touching data. - Per-team issue sequences —
teams.nextIssueNumberpowersENG-1,ENG-2, … without global counters. - Fractional
sortOrder— Board reorders write one document instead of renumbering a whole column. - Search + vector indexes in the schema — Full-text indexes on title/description, plus a 1536-dim vector index on
embeddingfor AI duplicate detection.
Option A: Vercel CLI
pnpm install -g vercel
vercelOption B: GitHub Integration
- Push your repo to GitHub
- Go to vercel.com/new
- Import your repository
- Add all environment variables from
.env.local - Deploy
npx convex deployThis deploys your Convex functions and schema to a production deployment. Set CLERK_FRONTEND_API_URL, CLERK_WEBHOOK_SECRET, and OPENAI_API_KEY in the Convex production dashboard too.
- All environment variables set in Vercel (including
NEXT_PUBLIC_vars) - All environment variables set in Convex production
- Clerk webhook endpoint updated to the production Convex HTTP Actions URL
- Clerk production API keys used (not development keys)
- Clerk Organizations, JWT template, and Billing configured on the production instance
- Test sign-up → create org → create issue → upgrade plan → AI chat, end-to-end
| Problem | Solution |
|---|---|
pnpm dev only starts one server |
The dev script uses npm-run-all to run dev:frontend and dev:backend in parallel. Run pnpm install again if it's missing. |
| Convex types not updating | Keep npx convex dev running. It generates types into convex/_generated/. |
| Page shows a loading state forever | Check that NEXT_PUBLIC_CONVEX_URL is correct in .env.local. |
| Problem | Solution |
|---|---|
| "Not authenticated" errors from Convex | The JWT template must be named exactly convex, and CLERK_FRONTEND_API_URL must be set on the Convex deployment. |
| Org pages 404 or redirect to onboarding | The JWT template needs the org_id / org_slug / org_role custom claims, and you must have an active organization selected. |
| Webhook returns 400 | Verify the signing secret matches CLERK_WEBHOOK_SECRET in Convex (not CLERK_SECRET_KEY). |
| User signs up but doesn't appear in Convex | Check the webhook endpoint URL ends with /clerk-webhook and uses the .convex.site domain. |
| Problem | Solution |
|---|---|
| Plan not updating after checkout | Make sure all subscription.* and subscriptionItem.* events are subscribed on the webhook. The plan syncs to organizations.plan in Convex. |
| AI page says upgrade required on a paid plan | AI access is gated by plan in Convex (hasAiAccess). Confirm the subscription webhook fired and the org's plan is pro or enterprise. |
| AI chat errors immediately | Set OPENAI_API_KEY on the Convex deployment: npx convex env set OPENAI_API_KEY sk-... |
| AI stops responding mid-day | Pro is rate-limited to 50 messages/user/day. Enterprise is unlimited. |
| Problem | Solution |
|---|---|
| "Index not found" error | Run npx convex dev to push your schema. Indexes are defined in convex/schema.ts. |
| Schema validation errors after changes | If you change field types, you may need to clear and re-seed data from the Convex dashboard. |
Already have the base app running? Here are some ideas to make it your own:
- 📥 Inbox & notifications — In-app notification feed for mentions, assignments, and status changes
- 🗺️ Roadmap view — Timeline/Gantt visualization of projects and target dates
- 📊 Cycle analytics — Burndown charts, velocity, and scope-change tracking
- 🔁 Issue templates & recurring issues — Standardize bug reports and rituals
- ✍️ AI issue drafting — Turn a one-line idea into a fully specced issue with acceptance criteria
- 🧹 Backlog grooming agent — Let the AI suggest stale issues to close and duplicates to merge
- 🗣️ Voice standup — Speech-to-text standup notes summarized into a cycle report
- 🔗 GitHub integration — Link PRs to issues and let the agent update statuses on merge
- 📈 Pagination — Cursor-based pagination for orgs with tens of thousands of issues
- 🌐 Public issue sharing — Read-only public links for individual issues
- 📦 Import/export — Bring in issues from CSV, Jira, or the real Linear's export format
- 💺 Per-seat metering — Track active seats and surface usage on the billing page
- 🏷️ Add-on features — Sell the AI agent as a standalone add-on with Clerk features
- 🎁 Trials — Time-boxed Pro trials for new organizations
This project's source code is shared under the Creative Commons Attribution-NonCommercial 4.0 International License for educational purposes only.
- ✅ Use this project for personal learning and education
- ✅ Fork it and modify it for non-commercial purposes
- ✅ Share it with others (with attribution)
- ✅ Use it as a portfolio project
- ✅ Reference it in blog posts and tutorials
- ❌ Use it for commercial purposes without a separate license
- ❌ Sell it or include it in a paid product
- ❌ Remove the attribution or license notice
"Vector" is a fictional name used for this educational demo. We do not claim any trademark, copyright, or intellectual property rights over this name. If "Vector" is a registered trademark belonging to another party, its use here is unintentional and purely for educational illustration. This project is not affiliated with, endorsed by, or connected to Linear, Atlassian (Jira), Asana, or any other project management platform or company. All third-party names and logos (Clerk, Convex, Vercel, Next.js, React, OpenAI, Tailwind CSS, TypeScript, etc.) are trademarks of their respective owners.
See the full LICENSE.md for details.
| Command | What it does |
|---|---|
pnpm dev |
Start both Next.js and Convex in parallel |
pnpm build |
Build the Next.js app for production |
pnpm start |
Start the production server |
pnpm lint |
Run ESLint across the project |
pnpm exec tsc --noEmit |
Type-check the whole project |
npx convex dev |
Start the Convex dev server (auto-generates types) |
npx convex deploy |
Deploy Convex to production |
npx convex dashboard |
Open the Convex dashboard in your browser |
npx convex env set KEY value |
Set an environment variable on the Convex deployment |
| Path | Purpose |
|---|---|
app/(marketing)/ |
Landing page and pricing page (public) |
app/(app)/[orgSlug]/ |
The workspace: team views, board, issues, projects, cycles, search, AI, settings |
app/onboarding/ |
Create-or-join-organization flow after sign-up |
app/sign-in/, app/sign-up/ |
Clerk auth pages |
convex/schema.ts |
Database schema — tables, indexes, search & vector indexes |
convex/http.ts + convex/webhooks.ts |
Clerk webhook endpoint and sync logic |
convex/lib/customFunctions.ts |
orgQuery / orgMutation wrappers (auth + org scoping) |
convex/lib/limits.ts |
Free-plan limit enforcement (assertCanCreateIssue, etc.) |
convex/agent/ |
AI agent: chat, tools, embeddings, triage, rate limiting |
components/ui/ |
shadcn/ui primitives |
components/shell/ |
App shell: sidebar, command palette wiring |
components/board/, components/issues/, components/issue-detail/ |
Board, list, and issue detail UI |
lib/plans.ts |
Single source of truth for Clerk plan IDs, slugs, and pricing display |
proxy.ts |
Clerk middleware for route protection (Next.js 16) |
.env.local |
Environment variables (not committed) |
- Route Groups —
(marketing)is the public site;(app)is the authenticated workspace. Each has its own layout. - Org-Scoped Routes — Everything in the workspace lives under
/[orgSlug]/.... The shell guarantees the user and org are synced before pages render. - Custom Convex Functions —
orgQuery/orgMutationresolvectx.user,ctx.org, andctx.membershipfrom the Clerk JWT and reject non-members. This is Convex's alternative to Row Level Security. - Clerk as Source of Truth — Users, orgs, memberships, and subscriptions live in Clerk and are mirrored into Convex via webhooks. Feature code never writes to those tables.
- Two-Layer Plan Gating —
has({ plan })/has({ feature })in the UI is cosmetic; the Convex helpers inconvex/lib/limits.tsare the real enforcement. proxy.tsMiddleware — Replaces traditionalmiddleware.tsin Next.js 16. Protects the app routes while keeping marketing pages public.