A modern Learning Management System (LMS) built with Next.js that provides real-time AI-powered voice teaching lessons for personalized learning experiences. Learnary combines voice AI (VAPI), Supabase, and Clerk to deliver interactive tutoring sessions with natural conversation.
Feature
Description
AI Teaching Lessons
Interactive AI tutors for subjects: Maths, Language, Science, History, Coding, Economics
Real-time Voice Sessions
Engage with your tutor through live voice conversation via VAPI
Personalized Journeys
Track lessons completed, lessons created, and session history
Lesson Library
Browse, search, and filter lessons by subject and topic
Lesson Builder
Create custom lessons with name, subject, topic, duration, style, and voice
Subscription Tiers
Plan limits (3 or 10 companions) with Clerk pricing; Pro = unlimited
Bookmarking UI
Bookmark button on lesson cards (UI ready)
Error Monitoring
Sentry integration for error tracking
Category
Technology
Framework
Next.js 15.3 (App Router, Turbopack)
Language
TypeScript
Auth
Clerk
Database
Supabase (PostgreSQL)
Voice AI
VAPI (@vapi-ai/web)
Styling
Tailwind CSS 4
UI Components
Radix UI, HeroUI, shadcn/ui (Button, Form, Input, Select, etc.)
Forms
React Hook Form + Zod
Animations
Framer Motion, Lottie React
Error Tracking
Sentry
Font
Bricolage Grotesque
flowchart TD
Start([Visitor arrives]) --> Home[/]
Home --> Auth{Authenticated?}
Auth -->|No| SignIn[/sign-in]
Auth -->|Yes| Dashboard[Dashboard /]
SignIn --> Clerk[Clerk Sign In]
Clerk --> Dashboard
Dashboard --> Nav[Navigation]
Nav --> HomeLink[Home]
Nav --> LessonsLink[Lesson Library]
Nav --> JourneyLink[My Journey]
HomeLink --> Home
LessonsLink --> Library[/companions]
JourneyLink --> Journey[/my-journey]
Library --> Search[Search & Filter by subject/topic]
Search --> Library
Library --> NewLesson[New Lesson]
NewLesson --> Builder[/companions/new]
Builder --> LimitCheck{Limit reached?}
LimitCheck -->|Yes| Upgrade[/subscription]
LimitCheck -->|No| Form[Lesson Builder Form]
Upgrade --> Pricing[Clerk Pricing Table]
Form --> Create[Create Companion]
Create --> Session[/companions/:id]
Library --> Card[Lesson Card]
Card --> Launch[Launch Lesson]
Launch --> Session
Session --> Voice[Voice Session - VAPI]
Voice --> Controls[Start / End Session, Mute]
Controls --> Transcript[Live Transcript]
Controls --> End[End Session]
End --> SaveSession[Save to session_history]
SaveSession --> Journey
Journey --> Stats[Lessons Completed / Lessons Created]
Stats --> Accordion[Accordion: Session History & Created]
Accordion --> Reopen[Reopen Lesson]
Reopen --> Session
Dashboard --> Recent[Recent Sessions]
Dashboard --> CTA[Build New Lesson CTA]
CTA --> Builder
Loading
Landing : User visits / (Dashboard or redirect if unauthenticated).
Auth : Signs in via Clerk at /sign-in.
Browse : Uses Home, Lesson Library (/companions), and My Journey (/my-journey).
Search : Filters lessons by subject and topic on the library page.
Create : Goes to Lesson Builder (/companions/new); if limit reached, directed to /subscription.
Learn : Launches a lesson from a card → /companions/[id] → starts VAPI voice session.
Interact : Uses Start/End Session, mute, and sees live transcript.
Track : Completed sessions appear in My Journey.
Learnary/
├── app/
│ ├── layout.tsx # Root layout (ClerkProvider, font, grid bg)
│ ├── page.tsx # Dashboard (home)
│ ├── globals.css # Global styles
│ ├── global-error.tsx # Global error boundary
│ ├── sign-in/
│ │ └── [[...sign-in]]/
│ │ └── page.tsx # Clerk sign-in
│ ├── companions/
│ │ ├── page.tsx # Lesson Library (search, filter, grid)
│ │ ├── new/
│ │ │ └── page.tsx # Lesson Builder (form or limit upsell)
│ │ └── [id]/
│ │ └── page.tsx # Lesson session (VAPI voice)
│ ├── my-journey/
│ │ └── page.tsx # Profile: sessions & created lessons
│ ├── subscription/
│ │ └── page.tsx # Clerk PricingTable
│ ├── api/
│ │ └── sentry-example-api/
│ │ └── route.ts # Sentry test API
│ └── sentry-example-page/
│ └── page.tsx # Sentry test page
├── components/
│ ├── Navbar.tsx # Main nav + Sign In / UserButton
│ ├── NavItems.tsx # Home, Lessons, My Journey links
│ ├── LessonCard.tsx # Lesson card with Launch + bookmark
│ ├── LessonList.tsx # Table of lessons (recent/created/completed)
│ ├── LessonForm.tsx # Lesson builder form
│ ├── LessonComponent.tsx # VAPI voice session UI + transcript
│ ├── SearchInput.tsx # Topic search (debounced)
│ ├── SubjectFilter.tsx # Subject dropdown filter
│ ├── Cta.tsx # CTA "Build New Lesson"
│ ├── Footer.tsx
│ └── ui/
│ ├── accordion.tsx
│ ├── button.tsx
│ ├── command.tsx
│ ├── dialog.tsx
│ ├── form.tsx
│ ├── input.tsx
│ ├── label.tsx
│ ├── popover.tsx
│ ├── select.tsx
│ ├── table.tsx
│ └── textarea.tsx
├── lib/
│ ├── actions/
│ │ └── companion.actions.ts # Server actions for companions & sessions
│ ├── supabase.ts # Supabase client (uses Clerk token)
│ ├── vapi.sdk.ts # VAPI client
│ └── utils.ts # cn, getSubjectColor, configureAssistant
├── constants/
│ ├── index.ts # subjects, subjectsColors, voices, recentSessions
│ └── soundwaves.json # Lottie animation for voice
├── types/
│ ├── index.d.ts # Companion, CreateCompanion, etc.
│ └── vapi.d.ts # VAPI type augmentations
├── public/
│ ├── icons/ # SVG icons (bookmark, cap, clock, subjects, etc.)
│ └── images/ # cta.svg, limit.svg
├── middleware.ts # Clerk auth middleware
├── next.config.ts # Sentry, image domains (img.clerk.com)
├── tailwind.config.js
├── tsconfig.json
├── postcss.config.mjs
├── instrumentation.ts # Sentry server
├── instrumentation-client.ts # Sentry client
├── sentry.edge.config.ts
├── sentry.server.config.ts
├── components.json # shadcn config
└── package.json
Node.js 20+
npm or pnpm
Supabase project
Clerk application
VAPI account and web token
# Clone the repository
git clone < repo-url>
cd Learnary
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env.local
# Edit .env.local with your keys
Open http://localhost:3000 .
Create .env.local with:
Variable
Description
Required
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
Clerk publishable key
Yes
CLERK_SECRET_KEY
Clerk secret key
Yes
NEXT_PUBLIC_SUPABASE_URL
Supabase project URL
Yes
NEXT_PUBLIC_SUPABASE_ANON_KEY
Supabase anon/public key
Yes
NEXT_PUBLIC_VAPI_WEB_TOKEN
VAPI web SDK public token
Yes
NEXT_PUBLIC_SENTRY_DSN
Sentry DSN (optional)
No
Column
Type
Description
id
uuid
Primary key
author
string
Clerk user ID
name
string
Lesson name
subject
string
maths, language, science, history, coding, economics
topic
string
Lesson topic/description
duration
number
Duration in minutes
style
string
casual / formal
voice
string
male / female
created_at
timestamp
Creation time
Column
Type
Description
id
uuid
Primary key
companion_id
uuid
FK → companions
user_id
string
Clerk user ID
created_at
timestamp
Session completion time
Route
Description
Auth
/
Dashboard: featured lessons + recent sessions + CTA
Optional
/companions
Lesson library with search and subject filter
Optional
/companions/new
Lesson builder or limit upsell
Required
/companions/[id]
Voice session page
Required
/my-journey
Profile, stats, lessons completed/created
Required
/sign-in
Clerk sign-in
No
/subscription
Clerk PricingTable
Optional
Component
Role
Navbar
Logo, NavItems, Sign In / UserButton
NavItems
Home, Lessons, My Journey (active state)
LessonCard
Card with subject badge, topic, duration, Launch Lesson, bookmark
LessonList
Table of lessons (name, topic, subject, duration)
LessonForm
Zod-validated form: name, subject, topic, duration, style, voice
LessonComponent
VAPI session: Start/End, mute, Lottie animation, transcript
SearchInput
Debounced topic search (URL params)
SubjectFilter
Subject dropdown (URL params)
Cta
“Build New Lesson” CTA with link to builder
All in lib/actions/companion.actions.ts:
Action
Purpose
createCompanion(formData)
Insert new companion (author from Clerk)
getAllCompanions({ limit, page, subject, topic })
List companions with filters
getCompanion(id)
Get single companion by id
addToSessionHistory(companionId)
Insert session record on call end
getRecentSession(limit)
Recent sessions (for dashboard)
getUserSession(userId, limit)
Sessions for a user
getUserCompanions(userId)
Companions created by user
newCompanionPermissions()
Check plan limit (Pro = unlimited, else 3 or 10)
Service
Usage
Clerk
Auth, UserButton, SignIn, pricing (plans)
Supabase
companions and session_history storage
VAPI
Voice AI: 11labs voices, Deepgram transcriber, GPT-4
Sentry
Error monitoring and source maps
Lottie
Soundwave animation during voice session
Clerk : Primary color #1E3A8A
Images : Remote patterns include img.clerk.com
Sentry : Org gurveer-20, project javascript-nextjs
Voice : Male/female + casual/formal (see constants/index.ts)
Script
Command
Description
dev
next dev --turbopack
Development server
build
next build
Production build
start
next start
Start production server
lint
next lint
Run ESLint
Lesson routes use /companions internally; renaming to /lessons would require redirects and import updates.
Bookmark UI is present on LessonCard; backend persistence is not implemented.
Supabase client uses Clerk JWT via auth().getToken() for RLS-friendly access.