© 2026 All rights reserved -
Software Engineer with 7+ years, delivering 219+ projects for clients across 10+ countries. Specialized in Laravel, web systems, and AI-integrated development.

A behind-the-scenes look at the engineering decisions behind engmoh.com — from headless architecture and three-tier caching to AI integration, custom bilingual i18n, and full SEO engineering.
Every developer eventually faces this question: do you use a pre-built portfolio template, or do you build your own from scratch? For most people, a template is the sensible answer. But for a software engineer whose primary claim is that they can architect and build complex, scalable web systems — using a template would be a contradiction.
So I built engmoh.com from scratch. This article walks through the architectural decisions, the technical challenges, and the engineering principles that shaped the platform from the ground up.
The core architectural decision was to decouple the content management layer from the presentation layer entirely. This is known as a Headless CMS approach.
The backend is built on Laravel 13 with PHP 8.3. It serves as a pure REST API — responsible for data management, business logic, authentication, and content delivery. The admin panel is powered by Filament v5, which provides a polished, fully-featured management interface without requiring any custom frontend development for the CMS itself.
Key backend capabilities include:
The public-facing website runs on Next.js 15 with React 19 and the App Router. This combination unlocks React Server Components — which means most page rendering happens on the server, reducing the JavaScript payload shipped to the browser and dramatically improving initial load performance.
Why Next.js specifically? Three reasons:
Performance was a non-negotiable requirement. The caching architecture operates at three independent layers:
Static assets (images, fonts, JavaScript bundles) are cached at the browser level with long-lived cache headers. Content that rarely changes does not need to round-trip to a server on every visit.
API responses fetched by Server Components are cached using Next.js's built-in unstable_cache. These caches are invalidated on-demand via a webhook that fires whenever content is updated in the CMS admin panel. This means: the site is fast like a static site, but content updates go live within seconds of being published — no full rebuild required.
For data fetched on the client side, a lightweight module-level cache stores results in memory during the page session. This prevents duplicate network requests when navigating between routes that share the same data.
The result: most pages load from cache in under 100ms, with no stale content problem because the invalidation chain fires automatically on every publish.
Supporting both Arabic and English — including right-to-left text direction for Arabic — is a core requirement. Rather than adopting a heavy i18n library, I implemented a custom translation system tailored to how the site actually works.
The selected language is stored in a browser cookie. On the server side, Next.js reads this cookie during the request and selects the appropriate language content from the API response — since every content field from the Laravel backend already contains both languages as separate values. On the client side, language switching fires a Custom Browser Event that is caught by a useTranslation hook, causing the UI to update without a full page reload and without duplicating any fetch calls.
The dir attribute on the HTML root element switches dynamically between ltr and rtl, which combined with Tailwind CSS's rtl: variant utilities, handles the entire layout mirroring automatically.
AI is not a gimmick in this project — it is integrated as functional infrastructure at multiple points in the workflow.
The admin panel supports audio uploads that are automatically transcribed using AI voice recognition. This makes it practical to capture long-form Arabic content by speaking rather than typing — something that matters in a bilingual workflow where typing Arabic can be slower.
When creating blog posts or project descriptions, an AI assistant can generate initial drafts based on structured prompts. These drafts are starting points — the final content is always reviewed and edited manually before publishing. The goal is speed of iteration, not hands-off generation.
AI calls are handled asynchronously via Laravel queues, so they never block the admin interface. Results are written back to the model when ready, and the admin panel notifies the user via real-time Livewire updates. This keeps the experience responsive regardless of how long an AI operation takes.
A portfolio website that does not rank is a portfolio that does not get seen. SEO was treated as an engineering discipline, not an afterthought.
<title>, <meta description>, canonical URL, Open Graph tags, and Twitter Card tags based on the actual content of that pagePerson, WebSite, Article, Service — helping search engines understand the semantic structure of the contentlastmod timestampsThe blog section of the site ships with two themes: a dark theme at /blog and a light theme at /blog-white. Both are functionally identical and serve different reading preferences. The light theme variant is blocked in robots.txt to prevent duplicate content from splitting SEO signals — canonical URLs point to the primary dark theme version.
Theme switching uses CSS custom properties rather than conditional class logic, which means the theme changes with a single root-level attribute swap rather than re-rendering any components.
For reference, here is the full stack used to build engmoh.com:
In the spirit of honest engineering reflection — a few decisions I would revisit:
TypeScript from day one. The frontend was started without strict TypeScript enforcement, and adding it incrementally is always more painful than starting with it. The type safety it provides, especially across Server/Client Component boundaries in Next.js, is worth the initial setup cost.
Earlier investment in end-to-end testing. The bilingual system and the cache invalidation pipeline both have a lot of moving parts. Playwright tests covering critical paths would have caught regressions faster during development.
Neither of these is a fundamental flaw — they are the kind of tradeoffs that happen when a single engineer is moving fast on a project. They are documented here because they are the lessons worth carrying into the next project.
Building your own portfolio is not the right decision for every developer. But for someone whose work involves designing systems for others, there is no clearer demonstration of capability than the platform they built for themselves.
Every architectural decision in engmoh.com was made intentionally, for specific technical or product reasons — not because it was familiar or easy. That is the standard I try to apply to client projects as well.
If you have questions about any part of the implementation, feel free to reach out. The contact details are on the site.
© 2026 All rights reserved -
Comments (3)
نورة السلمي
May 10, 2026Pinnedلقد طبقت ما شرحته وعمل بشكل مثالي من أول مرة. أنصح كل مطور يقرأ هذا.
خالد الشمري
May 19, 2026بالنسبة لسؤالك: نعم يعمل مع Laravel 9 وما فوق.
Gregg Leuschke
May 19, 2026بالنسبة لسؤالك: نعم يعمل مع Laravel 9 وما فوق.