Case Study·Client project·EdTech marketplace

Live at craft-music.com

We built Craft Music's lesson marketplace in Vue 3 & Nuxt: search, booking, payments and payouts.

Craft Music connects students with professional music teachers for live lessons. Our team built the core platform behind it: a server-rendered Nuxt site with teacher search and profiles, time-zone-safe booking and subscriptions, Stripe payments and Stripe Connect payouts, an admin panel and the NestJS API.

  • Vue 3.5
  • Nuxt 3
  • Pinia
  • PrimeVue 4
  • Tailwind
  • NestJS
  • PostgreSQL
  • Stripe Connect
  • AWS
Craft Music piano lessons page with search, filters for lesson type, price and availability, and a teacher card with intro video and trial booking
Real availability Filters only show teachers who are free in the student's time zone
Intro videos Uploaded from the browser, transcoded on AWS, streamed to every device
Booking Paid trial, single lesson or a weekly plan, all through Stripe
Since 2024
Working with Craft Music
Nuxt 2 → 3 migration first, then a new platform
3
Apps in the platform
public site · admin panel · API
36
Server-rendered instrument pages
searchable by real teacher availability
Feb 2026
Production launch
tagged releases with one-step rollback

From a brochure site to a two-sided marketplace

Craft Music matches students with vetted, professional music teachers for live online lessons anywhere in the US, and in-home lessons in select cities. Their previous site was a content site: it could show teachers, but not book or pay them.

We built the platform that turned it into a marketplace: students search by instrument, price and real availability, book a paid trial, then move to a weekly plan; teachers manage availability, run lessons and get paid out; and Craft Music's team runs everything from an admin panel.

It's a client project, so this page shows the live website and describes our engineering without publishing the client's code.

Client
Craft Music: live online and in-home music lessons in the US
Our role
UI design, public site, admin panel, API, payments, CI/CD
Public site
Nuxt 3 · Vue 3.5 · Pinia · PrimeVue 4 · Tailwind · hybrid SSR
Admin
Nuxt 3 SPA · PrimeVue 4 · Pinia
Backend
NestJS 11 · Prisma · PostgreSQL · AWS S3, SQS, MediaConvert
Integrations
Stripe & Stripe Connect · Stream Chat · Resend · Google Calendar · Mapbox
Status
Live at craft-music.com

A migration first, then a marketplace, then a long-term partnership

This is how we worked with Craft Music, and it's how we approach client products: earn trust on a contained project, agree the scope and design, build the core, get the money right, launch, and keep going. Dates come from the project's git history.

Craft Music timelineFeb 2024 → 2026
FEB 2024 Nuxt 2 → 3 migration
FEB 2025 Platform build starts
JUN 2025 Video lessons & chat
OCT 2025 Payouts & refunds
FEB 2026 Production launch
2026 Calendar, coupons, referrals
01

Fix the foundation first

Feb 2024
On Craft Music

Our first job for Craft Music was upgrading their existing site from Nuxt 2 to Nuxt 3, including the pre-rendering setup. It gave the client a supported framework without a rewrite, and gave us a working relationship before the bigger build.

What you get
  • A migration plan that keeps the site live
  • Framework upgrades without a rewrite
  • Pre-rendering and SEO that still work afterwards
02

Scope and design

Before the build
On Craft Music

The new platform was scoped in a statement of work: a marketplace where students browse professional teachers, filter by instrument and availability, and book live lessons. Design went through consultation, mockups and wireframes, client review and revisions until the Figma design was approved. That approved design became the reference for every feature.

What you get
  • A written scope both sides sign off on
  • Wireframes and mockups reviewed with you
  • An approved design before development starts
03

Build the core marketplace

Feb – Jun 2025
On Craft Music

The API, public site and admin panel were built side by side, starting with the parts every other feature depends on.

  1. Accounts, Google and Facebook sign-in
  2. Teacher onboarding, weekly availability, booking and subscription APIs
  3. Time-zone service, teacher profiles, blog
  4. Video lesson room and student–teacher messaging
What you get
  • Working features in a staging environment, not slideshows
  • Clean, review-ready pull requests
  • Async daily updates and a weekly progress review
04

Make the money flows correct

Aug – Dec 2025
On Craft Music

A marketplace is only as trustworthy as its money handling. This phase covered teacher payouts through Stripe Connect, signed Stripe webhooks, cancellation and refund rules, teacher applications, and admin tools for refunds, reviews and support reports. It ended with hardening the booking flow against two people grabbing the same slot.

What you get
  • Payment flows tested against real edge cases
  • Admin tools for refunds and disputes
  • Concurrency issues fixed before launch, not after
05

Launch to production

Jan – Mar 2026
On Craft Music

Production got its own pipeline: version tags build Docker images in GitHub Actions and deploy them to AWS, with the previous image kept for rollback. The production domain switched over in early February 2026, and tagged releases have shipped since.

What you get
  • CI/CD with versioned releases
  • Separate staging and production
  • A rollback path that takes one step
06

Keep extending

2026
On Craft Music

After launch we kept adding what the business needed: contract e-signing in teacher onboarding, a video processing pipeline, Google Calendar events for booked lessons, coupons and referrals.

  1. Teacher contract e-signing, stored as PDF
  2. Video pipeline, Google Calendar events for lessons
  3. Coupons with redemption limits, referrals
What you get
  • A team that stays after launch
  • Features prioritised with the business, not just the backlog

Eight problems every marketplace hits, and how we solved them

Booking and payments look simple until real users, real money and real time zones arrive. These are the problems we solved on Craft Music, described without exposing the client's code.

01

Booking across time zones that survives daylight saving

Time zonesSchedulingNestJS
Problem
A teacher in Los Angeles sets availability, a student in New York books it, and both clocks shift for daylight saving on different weeks than the student's calendar. Storing a single UTC offset puts lessons an hour off twice a year.
How we solved it
Availability is stored in the teacher's local time with their time zone, and each lesson is converted to UTC for its actual calendar date. Recurring plans, including every-other-week, are generated week by week, and the student's zone travels with every request so slots render correctly on their side.

Where this applies to you Any product with appointments, classes or shifts across regions: tutoring, healthcare, consulting, field services.

Stored as
Teacher local time + time zone
Converted
Per lesson date, so DST shifts land correctly
Plans
Trial, single, 1–5 per week, every other week
Viewer
Time zone sent with every API request
02

Search that filters on real availability

SearchPerformanceSSR
Problem
Students want "piano, online, under $60, free Tuesday evenings". Filtering on profile fields is easy. Filtering on whether a teacher is actually free at that time, in the student's time zone, without one query per teacher, is not.
How we solved it
Every candidate teacher's weekly hours are converted into the viewer's time zone and checked against existing bookings for the coming month, with all the data loaded in a few batched queries instead of per teacher. The results power 36 server-rendered instrument pages.

Where this applies to you Marketplaces and directories where "available now" matters more than static filters.

Filters
Instrument, lesson type, price, language, day and time, name
Sorting
6 sort options
Queries
Batched, not one per teacher
Pages
/lessons/[instrument], server-rendered
03

No double-booking, no stuck slots

ConcurrencyStripeScheduled jobs
Problem
Two students can try to book the same slot at once, and a student can open checkout and walk away. Without care, the first case double-books a teacher and the second blocks the slot forever.
How we solved it
Conflicts are checked for both teacher and student before payment, and an unpaid checkout only holds the slot briefly. A scheduled job asks Stripe whether each stale payment is still in progress, cancels it if not, and returns any credit and coupon use exactly once. Database indexes were added specifically for these checks.

Where this applies to you Ticketing, reservations, inventory and anything where two users can reach for the same thing.

Checks
Teacher and student conflicts before payment
Holds
Short hold for unpaid checkouts
Cleanup
Every 10 minutes, verified against Stripe
Release
Credit and coupon returned exactly once
04

One booking, one transaction: subscription, credit, coupon, commission

StripePrismaTransactions
Problem
A weekly plan touches everything at once: the Stripe subscription, the student's credit balance, a coupon, the platform's commission and the lessons themselves. A failure halfway leaves money and schedules out of sync.
How we solved it
Booking runs in a single database transaction that re-prices the coupon on the server, records what was charged and the teacher's payout base, locks in the commission rate, applies credit, and creates the lessons and the Stripe subscription. Discounts are attached so they repeat correctly on renewals.

Where this applies to you Subscription products with discounts, credits or revenue sharing, where billing bugs cost money and trust.

Atomic
One transaction for lessons, credit and pricing
Coupons
Re-priced server-side, limits safe under concurrency
Commission
Tiered, per-teacher overrides, locked at booking
Renewals
Discounts carried onto the Stripe subscription
05

Refunds that never overpay anyone

RefundsPayments
Problem
A cancelled lesson might have been paid partly by card, partly with credit, with a discount on top. Refund the wrong way and the platform loses money or the teacher is paid for a lesson that never happened.
How we solved it
Admin-configurable cancellation rules decide what is refunded. Card money is returned first, capped at what was actually charged, and the rest becomes credit. The teacher's payout is reduced proportionally, so a discounted cancelled lesson never overpays the teacher.

Where this applies to you Any product with cancellations, partial refunds or store credit.

Rules
Set by admins, not hard-coded
Order
Card first, capped at amount charged
Remainder
Returned as account credit
Teacher
Payout reduced proportionally
06

Teacher payouts with Stripe Connect

Stripe ConnectMarketplace
Problem
Students pay the platform, and teachers need to be paid out reliably, without anyone claiming the same earnings twice or a transfer failing silently.
How we solved it
Teachers onboard to Stripe Connect Express. Payout requests can't claim the same earnings twice, admins approve them after a check of the platform's Stripe balance, and transfers retry automatically on failure.

Where this applies to you Two-sided marketplaces that pay out sellers, creators, tutors or contractors.

Accounts
Stripe Connect Express onboarding
Requests
Minimum amount, no double-claims
Approval
Admin review with a balance check
Transfers
Automatic retries
07

Video uploads that work for real-world files

AWSStreamingUploads
Problem
Teacher intro videos come straight off phones and cameras in every format and size. Long videos fail on flaky connections if they upload in one request, and raw files play badly on mobile.
How we solved it
Videos upload straight from the browser to S3, in resumable parts with retries for large files. A queue triggers AWS MediaConvert to produce web-ready MP4 or adaptive streams and thumbnails, and the browser gets live progress over a socket instead of polling.

Where this applies to you Any product where users upload video or other large media.

Upload
Direct to S3, resumable multipart
Processing
SQS → MediaConvert → MP4 / HLS
Progress
Live updates over Socket.IO
Delivery
CDN, signed access where needed
08

A Nuxt site built to rank, with a page builder for the client's team

NuxtSEOPage builder
Problem
A lesson marketplace lives on search traffic: every instrument, teacher and article needs to be crawlable and fast. The client's team also needs to publish pages without a developer.
How we solved it
Rendering is chosen per route: a cached home page, server-rendered profiles and instrument pages, and client-only dashboards. Pages carry JSON-LD and canonical tags, and the sitemap is built from the API. A page builder, packaged as a Nuxt layer with 20 block types, lets admins compose pages that render on the server.

Where this applies to you Content-heavy Nuxt sites that need SEO and marketing autonomy at the same time.

Rendering
Per-route: cached, SSR or client-only
Structured data
JSON-LD and canonical tags per page
Sitemap
Generated from the API
Page builder
Nuxt layer, 20 block types

Two Nuxt apps, one API, managed services where they make sense

The public site and admin panel are separate Nuxt apps on one NestJS API. Payments, chat, email and media processing run on proven services instead of custom infrastructure.

Craft Music architecture: the Nuxt public site and Nuxt admin panel connect to a NestJS API with PostgreSQL, which integrates Stripe and Stripe Connect, Stream Chat, Resend email, Google Calendar and an AWS media pipeline.CLIENTSPublic siteNuxt 3 · hybrid SSRsearch · profiles · bookingcheckout · lesson room · chatAdmin panelNuxt 3 SPA · PrimeVue 4tutors · payments · refundscontent · coupons · settingsStudents & teachersweb · phone · email · calendarAPINestJS 11 APIauth · roles · social sign-inavailability · booking · plansrefunds · coupons · commissionscheduled jobs · webhooksDocker · GitHub Actions · AWSPostgreSQLPrisma · migrationsSERVICESStripe + Connectpayments · subscriptions · payoutsAWS mediaS3 · SQS · MediaConvert · CDNStream Chatstudent–teacher messagingResendtransactional email · remindersGoogle Calendarlessons pushed on booking
Built by our teamManaged servicesEnd users

What this project shows about our Vue work

Hybrid rendering

Per-route rules: the home page is regenerated every 60 seconds, profiles and instrument pages are server-rendered, and dashboards are client-only.

routeRules · Nuxt 3
Nuxt layers

The page builder lives in its own Nuxt layer with its own components, middleware and recursive renderer, keeping the main app clean.

layers/page-builder
SEO

Structured data and canonical tags per page, an API-driven sitemap, robots rules and llms.txt.

@nuxtjs/seo · sitemap
Performance tuning

Inlined CSS was switched off after measuring its weight per page, PrimeVue components are whitelisted, and vendor code is split into separate chunks.

nuxt.config · Vite
State and composables

Pinia stores per domain (booking, payments, auth, chat) and composables for Stripe, uploads, page meta and the viewer's time zone.

pinia/* · composables/*
Auth and roles

Email, Google and Facebook sign-in, with global route middleware sending students, teachers and admins to the right place.

middleware/*
Stripe Elements

One payment form handling both one-off payments and saved cards for subscriptions, plus Stripe Connect onboarding for teachers.

PaymentIntent · SetupIntent
Real-time UI

Stream Chat for messaging and a Socket.IO channel for live video-processing progress.

Stream Chat · Socket.IO
Admin SPA

A separate Nuxt admin for tutors, students, lessons, payments, refunds, coupons, reviews, content and settings.

admin.craft-music.com
PrimeVue 4 + Tailwind

A consistent component system across the public site and admin, themed to the client's brand.

PrimeVue 4 · Tailwind
Maps

A Mapbox map of in-home lesson coverage by ZIP code, built on US Census ZIP shapes.

Mapbox GL
Migrations

The client's previous site was moved from Nuxt 2 to Nuxt 3 by our team before the new platform was built.

Nuxt 2.15 → 3.10

Projects where this experience pays off

A two-sided marketplace

Profiles, search, booking, payments and payouts, with an admin team keeping quality up.

Stripe Connect payouts · reviews · admin approval

Booking and scheduling

Availability, time zones, recurring plans, rescheduling and calendar events, without double-bookings.

DST-safe scheduling · slot holds · Google Calendar

Subscriptions with real-world pricing

Trials, weekly plans, coupons, credits, commission and refunds that all add up.

One transaction per booking · server-side pricing

A Nuxt site that has to rank

Server-rendered landing pages, structured data and a CMS your marketing team can use without a developer.

36 SSR instrument pages · page builder · JSON-LD

Video and media

Large uploads, transcoding, streaming and live lesson rooms.

Resumable S3 uploads · MediaConvert · live progress

A legacy Nuxt 2 app

Moving to Nuxt 3 without freezing the business, then building on top of it.

Craft Music: Nuxt 2.15 → 3.10

Questions teams ask us

Is Craft Music a client project?

Yes. Craft Music is a client we've worked with since 2024: first a Nuxt 2 to Nuxt 3 migration of their existing site, then the new lesson platform. This page describes the parts our team built and shows the live website, not the client's source code.

Can you build a booking or scheduling product?

Yes. Craft Music handles teacher availability, student time zones, recurring weekly plans, rescheduling, cancellations and Google Calendar events, with protection against double-booking. The same patterns apply to clinics, coaching, rentals and field services.

Do you handle payments and payouts?

Yes. On Craft Music we built one-off payments, Stripe subscriptions, coupons, credits, refunds and teacher payouts through Stripe Connect, with signed webhooks and admin tools for the finance side.

Do you only do frontend?

Vue and Nuxt are our core, but this project included the NestJS API, PostgreSQL schema, AWS media pipeline and CI/CD. When your frontend work touches the backend, we can handle both.

Can you migrate our Nuxt 2 app?

Yes. Our work with Craft Music started with exactly that: moving their site from Nuxt 2 to Nuxt 3. Migrations and long-term maintenance are core VueSquad work.

How do we start?

Send us a short note on the contact page about what you're building and where you're stuck. We'll set up a call to go through your product and codebase.

Get started

Building a marketplace or booking product in Vue?

Talk to the engineers who built Craft Music's search, booking, payments and payouts. Tell us what you're building and where it's stuck.

Matched in 24 hours·48 hours free on every plan·No long-term contract