02Side project2025 – 2026
LokalWeb
A website-as-a-service for Kosovo’s small businesses
Most small businesses in Kosovo exist only on Instagram or TikTok. The opening hours are in a story that expired last week. LokalWeb asks the owner five questions in Albanian and gives the business a website on its own subdomain, with a booking calendar if it needs one.
- Role
- Solo: product, design, full-stack
- Status
- Working prototype, built solo
- Stack
- Next.js, TypeScript, Supabase, Claude API, Vercel
- Live
- Landing page
- from business description to website
- 5 steps
- a brand brief, then the theme
- 2 model calls
- per business, resolved in middleware
- 1 subdomain
- Supabase schema with row-level security
- 18 migrations
The problem
Barbershops, restaurants, clinics, salons: most of Kosovo’s small businesses have an Instagram page and no website. Hours, services and prices live in posts and stories, and a customer who wants to book sends a message and waits. A proper website costs a designer, a developer and a few weeks; most owners have none of the three.
LokalWeb began as my second-year software engineering project at UMIB and kept growing into a product: a business describes itself in plain words, and a few minutes later it has a website on its own subdomain, in Albanian, with a booking system if the business takes appointments.
The flow
- Industry, city, and the one thing that makes the business different.
- A description in the owner’s words, and the services with price and duration.
- A layout for each section, or “let the AI decide” per section.
- A visual archetype: eight presets, custom colours, or AI.
- Tone and language. Then Generate.
Decisions
- Two calls, not one. First a brand brief (Claude Haiku, temperature 0.3, a strict JSON schema: positioning, three defining traits, target customer, voice, cultural anchor). Then theme and content generation (Claude Sonnet) working from that brief. Separating strategy from execution made the output more consistent and much cheaper to iterate on.
- Structured output only. The renderer composes sections from typed JSON payloads. No free text from a model reaches the page unparsed.
- A tenant per subdomain. Next.js middleware reads the host header and rewrites a business subdomain to its tenant routes; the main domain serves the wizard and the dashboard.
- Supabase Auth with row-level security. Businesses, profiles, services, bookings. Eighteen migrations, including one called rls_tightening, which is the honest name for a day spent fixing my own policies.
- Booking as the upsell. Timezone-aware slot generation, a uniqueness constraint against double bookings, WhatsApp confirmations. Walk-in businesses get a contact call-to-action instead.
- Albanian where the owner types. The wizard is written in Albanian, with examples in the way people actually describe their shops. The generated site follows the language chosen in step five. The dashboard is still a mix of Albanian and English, which is on the list.
- A wizard harness. Fixtures of real business types run through the pipeline on demand, so a change in the prompts shows up as a visible difference in the output, not a feeling.
Where it stands
The core flow works end to end: a barber can describe the shop and get a site on a subdomain with services and a booking calendar. The generated output is uneven, and it needs real product work on layouts and copy before a paying customer should see it.
Work and university both became full-time in 2026 and the scope kept growing, so active development is on hold. It is the most ambitious thing I have built alone, and the clearest lesson in scope I have had.
What I learned
- Structured output is the only kind a renderer should trust.
- Scope is a product decision, not a technical one.
- Stopping while it still works beats letting it decay.