01 / 2025
EgyStay
Production rental marketplace with real payments — bilingual EN/AR, map-driven search, real-time chat, and streaming-SSR performance work, shipped end-to-end.
RoleFront-End Engineer
TimelineApril 2025 – present

Overview
EgyStay is Lesoll's Airbnb-style short-term rental platform for Egypt — a live business with real users and real money flowing through it, not a demo or a clone tutorial. Guests book Nile-view apartments and Red Sea villas; hosts and co-hosts manage listings through dedicated dashboards. I'm the primary frontend engineer: I've authored ~70% of all commits (614 of 885 — the rest are CI bots and one teammate) across roughly 63,000 lines of code in 600+ source files, 28+ custom React hooks, three role-based dashboards, and two languages. I own the architecture, performance, SEO, and the full feature surface.
The challenge
Rental marketplaces layer calendar availability, geo-search, multi-role permissions, and payment checkout on top of standard marketplace complexity — in two languages with full RTL. Map pan and zoom must sync to the URL and API without triggering RSC refetch loops. Heavy client widgets — calendar, maps, chat — must not tank LCP on a full-viewport hero. And all of it had to keep shipping while I migrated the codebase to TypeScript underneath a live product.
What I did
- Built the full booking lifecycle with real money: search → quote with service fees, promo discounts, and host commission → Paymob checkout → reservation management → reviews — including quote locking so the price can't change mid-checkout, instant-booking vs host-approval branches, and long-stay rates
- Cut homepage LCP by streaming the hero before API data — React Suspense boundaries around featured and personalized sections, above-the-fold Framer Motion replaced with pure CSS, WebP hero assets, and a lazy-loaded calendar
- Led a phased TypeScript migration on the live product — introduced an ESLint 9 + Prettier + Husky + lint-staged baseline, then migrated the entire services layer in planned phases without pausing feature delivery
- Shipped map-driven search — MapLibre with Supercluster clustering, debounced bounds flowing through Redux to URL to API, 12+ filters, and a fix for an infinite navigation loop caused by floating-point coordinate churn
- Delivered Socket.io real-time chat — paginated history, typing indicators, presence, delivery tracking and read receipts, and booking events rendered as interactive action cards instead of plain text
- Built the product bilingual from day one — ~2,300 translation keys per locale with next-intl, RTL document flipping, RTL-aware calendars and dialogs, and multi-currency pricing with live conversion and cookie/profile precedence rules
- Architected three authenticated experiences — guest, host, and co-host route groups behind server-side guards, URL-driven auth modals (login, register, and OTP without page navigation), and a Google OAuth token-refresh queue that intercepts 401s and replays requests after refresh
- Owned the 17-step host listing wizard — dynamic map step, @dnd-kit image reordering, and client-side canvas compression before upload to cut failures on slow mobile networks
- Shipped SEO as a growth feature — dynamic bilingual listing sitemaps, JSON-LD vacation-rental schema per page type, hreflang alternates, legacy-ID → slug 301 redirects, and a campaign short-link system for marketing attribution
Deep dive
Performance: streaming the homepage
I rebuilt the homepage around React Suspense streaming: the hero renders immediately while featured and personalized sections stream in behind it, which cut LCP. Above-the-fold Framer Motion animations became pure CSS so JavaScript stops blocking first paint, hero assets converted to WebP, and the date-picker calendar is dynamically imported with ssr: false so it never blocks the hero. Anonymous visitors skip auth-only API calls entirely, scroll-listener re-registration leaks got fixed, and hot-path handlers are memoized.
Bookings & payments
The full lifecycle from search to review: date and guest selection, a quote covering service fees, promo discounts, host commission, and long-stay rates, then Paymob checkout with card validation. Quotes lock at checkout so the price can't shift mid-payment. Instant-booking and host-approval listings branch into different flows, and booking events feed back into chat as interactive action cards.
Map-driven search — and its best bug
Search runs on a custom ~1,100-line MapLibre abstraction with Supercluster price-labeled pins that spread coincident coordinates at max zoom. The URL is the source of truth — Redux syncs from the address bar, not the other way around — so every search state is shareable and back-button safe. The best bug: an infinite navigation loop caused by floating-point coordinate churn between the map and the URL. Canonicalizing query strings in a useSearchNavigation hook killed it.
Real-time chat & inbox
A custom useSocketChat hook manages room lifecycle, message deduplication, and AbortController race protection when users switch conversations quickly. Messages carry delivery tracking and read receipts; booking events render as structured action cards, not plain text. On desktop, a resizable 3-pane layout separates the chat list, message thread, and booking context panel; mobile switches between views. Co-host room detection routes messages to the right participant set, and everything is localized in both languages.
Arabic RTL, done properly
Full bilingual product with next-intl: ~2,300 translation keys per locale, locale-first routing (/en, /ar), dir="rtl" document flipping, RTL-aware dialogs, layouts, and calendars, and mirrored hero imagery for Arabic. Currency resolution follows precedence rules across cookie and profile preferences, unified between the client and server API layers. Genuine Arabic RTL support is rare in portfolios — and load-bearing for a product serving the MENA market.
TypeScript migration on a moving train
The codebase started as JavaScript. I introduced an industrialization baseline first — ESLint 9 with jsx-a11y, Prettier, Husky, lint-staged, and a typecheck script in CI — then migrated the entire services layer to TypeScript in planned phases, all without stopping feature delivery on a production site. Incremental-migration judgment, not greenfield-only experience.
The result
A production marketplace for a real company (Lesoll) with real payments flowing through it — over a year of continuous shipping across booking, search, chat, host tooling, and SEO, while migrating the codebase to TypeScript and cutting LCP along the way.