Why this site runs on Laravel now
The first version of this site was a React and TypeScript app built with Vite. It worked, but every piece of copy lived inside a 650-line component, and adding a page meant adding a router. I build most of my client work in Laravel, so it made sense for my own site to run on the same stack.
What moved where
The port kept the structure of the original and changed the tooling underneath it.
- Content moved out of JSX constants into
config/portfolio.php. Skills, projects, experience, the hero copy, even the theme list are plain PHP arrays. Editing the site is editing that one file. - Markup became Blade. Repeated pieces are anonymous components: cards, the section header, the timeline entry, the icon set.
- Styles stayed as one stylesheet, now organised around semantic tokens so the whole site can be re-skinned by overriding a handful of variables.
- Motion moved from React hooks to small vanilla modules driven by anime.js, with a lazy-loaded Three.js scene behind the hero.
Why not keep React
There was no state to manage. A portfolio is content with some motion on top, and the motion does not need a virtual DOM to run. Blade renders the page on the server, the browser gets finished HTML, and JavaScript only adds behaviour. The page is fully readable with scripts disabled, which was never true of the React version.
What Laravel adds
Once the site was a Laravel app, the things a static page usually lacks came cheaply:
- A generated
sitemap.xmlandrobots.txtthat follow the configured domain. - Feature tests that render every page and assert the content, the animation hooks, and the SEO output.
- This blog: Markdown files in a folder, parsed and cached by a small repository class.
Route::get('/blog/{slug}', [BlogController::class, 'show'])
->where('slug', '[a-z0-9-]+')
->name('blog.show');
No database is involved. Sessions and cache use the file driver, so deploying is copying the code and running the asset build.
The build pipeline
Vite still handles assets, through the official Laravel plugin. npm run build writes hashed files into public/build, and the @vite directive in the layout picks them up. The Three.js scene is imported dynamically so it ships as its own chunk and loads after the page is interactive.
If you are holding a React portfolio that is really just content, this is a comfortable place to land.