Personal portfolio for Mohammed Alajmi (@DevM7mdAli) — software engineer, full-stack developer, and app developer.
Live at mohammed-alajmi.me.
- Bilingual — English and Arabic, including full RTL layout, via
react-i18next. - Light/dark theme with a persisted preference (
zustand+localStorage). - Dynamic content — projects and work experience are fetched from Supabase rather than hardcoded, with categories and tags for filtering.
- Animated, responsive UI built with Tailwind CSS and Framer Motion.
- Contact form wired to Formspree.
- SPA routing on GitHub Pages — a
404.htmlredirect trick restores the original path so React Router's own not-found page renders instead of silently falling back to the homepage.
- Vite + React 18 + TypeScript
- Tailwind CSS (with Material Tailwind)
- React Router
- Supabase — projects & experience data (Postgres + PostgREST)
- TanStack Query — data fetching/caching
- Zustand — UI state (theme, language)
- Framer Motion — animations
- react-i18next — localization
- Formspree — contact form backend
- Node.js 18+
- pnpm
- A Supabase project (for projects/experience data)
pnpm installCopy .env.example to .env and fill in your Supabase project's URL and anon/public key:
cp .env.example .envVITE_SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-public-key
The anon key is read-only by design (enforced via Row-Level Security) — the site only ever reads projects, experiences, project_categories, tags, and project_tags. See supabase/ for the SQL used to seed that data; run those scripts in the Supabase SQL editor with an account that has write access.
pnpm devOpen http://localhost:5173.
| Command | Description |
|---|---|
pnpm dev |
Start the Vite dev server |
pnpm build |
Type-check-free production build to dist/ |
pnpm preview |
Preview the production build locally |
pnpm deploy |
Build and publish dist/ to GitHub Pages (gh-pages) |
src/
├── components/ # UI sections (About, Skills, Experience, projects, ContactMe, NavBar, Footer, Error)
├── data/ # Static profile data (links, resume, etc.)
├── i18n/ # react-i18next setup + en/ar locale files
├── lib/ # Supabase client, shared types, data-fetching helpers
├── stores/ # Zustand stores (theme, language)
├── utils/ # Small shared helpers (typed Framer Motion wrappers, etc.)
├── App.tsx # Router setup
└── main.tsx # Entry point
public/ # Static assets, including the GitHub Pages SPA 404 redirect
supabase/ # SQL seed/migration scripts for the projects & experience data
The site is a static build deployed to GitHub Pages via gh-pages, served under the custom domain in public/CNAME:
pnpm deploySince GitHub Pages has no server-side routing, public/404.html implements the rafgraph/spa-github-pages redirect trick, and index.html decodes it back into the real path before React Router mounts — so a bad URL correctly shows the app's own 404 page rather than bouncing to the homepage.