import { createClient } from '@supabase/supabase-js' const supabase = createClient(URL, KEY) export default async function Orders() { const { data: orders } = await supabase .from('orders') .select('id, table, items, status') .order('created_at', { ascending: false }) return ( <Dashboard title="Live orders"> {orders.map((o) => ( <OrderCard key={o.id} order={o} /> ))} </Dashboard> ) } // realtime: new orders slide in supabase.channel('orders') .on('postgres_changes', { event: 'INSERT' }, (p) => setOrders((prev) => [p.new, ...prev])) .subscribe() const theme = { orange: '#FF5A1F', blue: '#2F6BFF', ink: '#0F0E15', } export function useBooking(id: string) { const [slot, setSlot] = useState<Slot | null>(null) useEffect(() => { load(id).then(setSlot) }, [id]) return slot } import { createClient } from '@supabase/supabase-js' const supabase = createClient(URL, KEY) export default async function Orders() { const { data: orders } = await supabase .from('orders') .select('id, table, items, status') .order('created_at', { ascending: false }) return ( <Dashboard title="Live orders"> {orders.map((o) => ( <OrderCard key={o.id} order={o} /> ))} </Dashboard> ) } // realtime: new orders slide in supabase.channel('orders') .on('postgres_changes', { event: 'INSERT' }, (p) => setOrders((prev) => [p.new, ...prev])) .subscribe() const theme = { orange: '#FF5A1F', blue: '#2F6BFF', ink: '#0F0E15', } export function useBooking(id: string) { const [slot, setSlot] = useState<Slot | null>(null) useEffect(() => { load(id).then(setSlot) }, [id]) return slot }
import { View, Text, Pressable } from 'react-native' import Animated, { FadeInUp } from 'react-native-reanimated' export function Hero({ user }: Props) { return ( <Animated.View entering={FadeInUp.duration(600)}> <Text style={s.title}>Hi, {user.name}</Text> <Pressable onPress={startProject}> <Text style={s.cta}>Start a project →</Text> </Pressable> </Animated.View> ) } const s = StyleSheet.create({ title: { fontSize: 32, fontWeight: '800' }, cta: { color: '#FF5A1F', letterSpacing: 2 }, }) // api/contact.ts export async function POST(req: Request) { const body = await req.json() await sendWhatsApp(body.phone, body.message) return Response.json({ ok: true }) } type Project = { name: string stack: ('expo' | 'next' | 'supabase')[] shipped: boolean } const projects: Project[] = await getProjects() console.log(`deployed ${projects.length} apps`) $ npx expo start --tunnel › Metro waiting on exp://192.168.1.4:8081 import { View, Text, Pressable } from 'react-native' import Animated, { FadeInUp } from 'react-native-reanimated' export function Hero({ user }: Props) { return ( <Animated.View entering={FadeInUp.duration(600)}> <Text style={s.title}>Hi, {user.name}</Text> <Pressable onPress={startProject}> <Text style={s.cta}>Start a project →</Text> </Pressable> </Animated.View> ) } const s = StyleSheet.create({ title: { fontSize: 32, fontWeight: '800' }, cta: { color: '#FF5A1F', letterSpacing: 2 }, }) // api/contact.ts export async function POST(req: Request) { const body = await req.json() await sendWhatsApp(body.phone, body.message) return Response.json({ ok: true }) } type Project = { name: string stack: ('expo' | 'next' | 'supabase')[] shipped: boolean } const projects: Project[] = await getProjects() console.log(`deployed ${projects.length} apps`) $ npx expo start --tunnel › Metro waiting on exp://192.168.1.4:8081
$ git push origin main ✓ Build completed in 38s ✓ Deployed to production async function fetchStats() { const res = await fetch('/api/stats', { next: { revalidate: 60 }, }) if (!res.ok) throw new Error('stats failed') return res.json() as Promise<Stats> } export const metadata = { title: 'John Dev — Apps · Websites · Support', description: 'Built in Abu Dhabi', } create table bookings ( id uuid primary key default gen_random_uuid(), client text not null, starts_at timestamptz, status text default 'pending' ); alter table bookings enable row level security; function formatAED(n: number) { return new Intl.NumberFormat('en-AE', { style: 'currency', currency: 'AED', }).format(n) } <Route path="/projects/:slug" element={<Case />} /> // TODO: ship it ✦ export default App $ git push origin main ✓ Build completed in 38s ✓ Deployed to production async function fetchStats() { const res = await fetch('/api/stats', { next: { revalidate: 60 }, }) if (!res.ok) throw new Error('stats failed') return res.json() as Promise<Stats> } export const metadata = { title: 'John Dev — Apps · Websites · Support', description: 'Built in Abu Dhabi', } create table bookings ( id uuid primary key default gen_random_uuid(), client text not null, starts_at timestamptz, status text default 'pending' ); alter table bookings enable row level security; function formatAED(n: number) { return new Intl.NumberFormat('en-AE', { style: 'currency', currency: 'AED', }).format(n) } <Route path="/projects/:slug" element={<Case />} /> // TODO: ship it ✦ export default App

I design and build mobile apps, websites and dashboards that help growing businesses in the UAE run simpler.
Start a project→Simple software that saves businesses time — designed, built and supported by one person you can actually call.
import { createClient } from '@supabase/supabase-js' const supabase = createClient(URL, KEY) export default async function Orders() { const { data: orders } = await supabase .from('orders') .select('id, table, items, status') .order('created_at', { ascending: false }) return ( <Dashboard title="Live orders"> {orders.map((o) => ( <OrderCard key={o.id} order={o} /> ))} </Dashboard> ) } // realtime: new orders slide in supabase.channel('orders') .on('postgres_changes', { event: 'INSERT' }, (p) => setOrders((prev) => [p.new, ...prev])) .subscribe() const theme = { orange: '#FF5A1F', blue: '#2F6BFF', ink: '#0F0E15', } export function useBooking(id: string) { const [slot, setSlot] = useState<Slot | null>(null) useEffect(() => { load(id).then(setSlot) }, [id]) return slot } import { createClient } from '@supabase/supabase-js' const supabase = createClient(URL, KEY) export default async function Orders() { const { data: orders } = await supabase .from('orders') .select('id, table, items, status') .order('created_at', { ascending: false }) return ( <Dashboard title="Live orders"> {orders.map((o) => ( <OrderCard key={o.id} order={o} /> ))} </Dashboard> ) } // realtime: new orders slide in supabase.channel('orders') .on('postgres_changes', { event: 'INSERT' }, (p) => setOrders((prev) => [p.new, ...prev])) .subscribe() const theme = { orange: '#FF5A1F', blue: '#2F6BFF', ink: '#0F0E15', } export function useBooking(id: string) { const [slot, setSlot] = useState<Slot | null>(null) useEffect(() => { load(id).then(setSlot) }, [id]) return slot }
import { View, Text, Pressable } from 'react-native' import Animated, { FadeInUp } from 'react-native-reanimated' export function Hero({ user }: Props) { return ( <Animated.View entering={FadeInUp.duration(600)}> <Text style={s.title}>Hi, {user.name}</Text> <Pressable onPress={startProject}> <Text style={s.cta}>Start a project →</Text> </Pressable> </Animated.View> ) } const s = StyleSheet.create({ title: { fontSize: 32, fontWeight: '800' }, cta: { color: '#FF5A1F', letterSpacing: 2 }, }) // api/contact.ts export async function POST(req: Request) { const body = await req.json() await sendWhatsApp(body.phone, body.message) return Response.json({ ok: true }) } type Project = { name: string stack: ('expo' | 'next' | 'supabase')[] shipped: boolean } const projects: Project[] = await getProjects() console.log(`deployed ${projects.length} apps`) $ npx expo start --tunnel › Metro waiting on exp://192.168.1.4:8081 import { View, Text, Pressable } from 'react-native' import Animated, { FadeInUp } from 'react-native-reanimated' export function Hero({ user }: Props) { return ( <Animated.View entering={FadeInUp.duration(600)}> <Text style={s.title}>Hi, {user.name}</Text> <Pressable onPress={startProject}> <Text style={s.cta}>Start a project →</Text> </Pressable> </Animated.View> ) } const s = StyleSheet.create({ title: { fontSize: 32, fontWeight: '800' }, cta: { color: '#FF5A1F', letterSpacing: 2 }, }) // api/contact.ts export async function POST(req: Request) { const body = await req.json() await sendWhatsApp(body.phone, body.message) return Response.json({ ok: true }) } type Project = { name: string stack: ('expo' | 'next' | 'supabase')[] shipped: boolean } const projects: Project[] = await getProjects() console.log(`deployed ${projects.length} apps`) $ npx expo start --tunnel › Metro waiting on exp://192.168.1.4:8081
$ git push origin main ✓ Build completed in 38s ✓ Deployed to production async function fetchStats() { const res = await fetch('/api/stats', { next: { revalidate: 60 }, }) if (!res.ok) throw new Error('stats failed') return res.json() as Promise<Stats> } export const metadata = { title: 'John Dev — Apps · Websites · Support', description: 'Built in Abu Dhabi', } create table bookings ( id uuid primary key default gen_random_uuid(), client text not null, starts_at timestamptz, status text default 'pending' ); alter table bookings enable row level security; function formatAED(n: number) { return new Intl.NumberFormat('en-AE', { style: 'currency', currency: 'AED', }).format(n) } <Route path="/projects/:slug" element={<Case />} /> // TODO: ship it ✦ export default App $ git push origin main ✓ Build completed in 38s ✓ Deployed to production async function fetchStats() { const res = await fetch('/api/stats', { next: { revalidate: 60 }, }) if (!res.ok) throw new Error('stats failed') return res.json() as Promise<Stats> } export const metadata = { title: 'John Dev — Apps · Websites · Support', description: 'Built in Abu Dhabi', } create table bookings ( id uuid primary key default gen_random_uuid(), client text not null, starts_at timestamptz, status text default 'pending' ); alter table bookings enable row level security; function formatAED(n: number) { return new Intl.NumberFormat('en-AE', { style: 'currency', currency: 'AED', }).format(n) } <Route path="/projects/:slug" element={<Case />} /> // TODO: ship it ✦ export default App

I design and build mobile apps, websites and dashboards that help growing businesses in the UAE run simpler.
Start a project→About me
hello, world. I'm John. Developer · Abu Dhabi, UAE. I design, build and support apps, websites and dashboards for small businesses — and keep them running. Years in operations before code, now shipping with React Native, Expo, Next.js and Supabase.
What I do
- MOBILE APPS: iOS + Android · Expo
- WEBSITES: Fast, SEO-ready sites
- WEB APPS & DASHBOARDS: Dashboards · data at a glance
- UI/UX DESIGN: Figma → real product
- E-COMMERCE: Shops & online ordering
- SUPPORT & IT: Hosting, fixes, updates
SELECTED WORK
SELECTED WORK
HOW I WORK
One person from first call to launch — and after.
- DAY 1–3
Discover
A quick call to understand your business, your customers and what a win looks like. You get a clear scope and price.
CALLGOALSSCOPE - WEEK 1–2
Design
Wireframes, then a clickable Figma prototype you can tap through on your phone before a single line of code.
WIREFRAMESUIPROTOTYPE - WEEK 2–6
Build
I build it with React Native, Next.js and Supabase, and show you real progress every week.
WEEKLY DEMOSTESTINGLAUNCH - ONGOING
Support
After launch I host it, watch it and keep improving it. One contact, no hand-offs.
HOSTINGFIXESUPDATES
HOW I WORK
One person from first call to launch — and after.
- DAY 1–3
Discover
A quick call to understand your business, your customers and what a win looks like.
CALLGOALSSCOPE - WEEK 1–2
Design
Wireframes, then a clickable Figma prototype you can tap through before any code.
WIREFRAMESUIPROTOTYPE - WEEK 2–6
Build
React Native, Next.js and Supabase, with real progress shown every week.
WEEKLY DEMOSTESTINGLAUNCH - ONGOING
Support
After launch I host it, watch it and keep improving it. One contact, no hand-offs.
HOSTINGFIXESUPDATES
BY THE NUMBERS
Small team of one. Real things shipped.
BY THE NUMBERS
Small team of one. Real things shipped.
LET'S BUILDsomething good.
Tell me what you want to build. You'll talk to me directly — from the first message to launch day and after.
PLACEHOLDER — add WHATSAPP_NUMBER in app/_lib/site.tsMessage me on WhatsApp →See projects ↑AVAILABLE FOR NEW PROJECTS — ABU DHABILET'SBUILDsomethinggood.
Tell me what you want to build. You'll talk to me directly — from the first message to launch day and after.
PLACEHOLDER — add WHATSAPP_NUMBER in app/_lib/site.tsMessage me on WhatsApp →See projects ↑AVAILABLE — ABU DHABI




