Build a Fullstack App with Laravel 12, React, and TypeScript (Vite-Powered)


In this tutorial, you'll learn how to build a modern fullstack application using Laravel 12 as an API backend, and React + TypeScript for the frontend, powered by Vite — all inside a single project.
🧱 What We'll Use
- Laravel 12.x – API-first PHP backend
- React 18+ – For building a dynamic, SPA-style frontend
- TypeScript – Type-safe JavaScript for better dev experience
- Vite – Fast dev server and build tool, replacing Webpack
- Single project structure – Laravel serves both the API and React frontend
📦 Step 1: Create a Laravel Project
laravel new my-app
cd my-app
This creates a new Laravel 12 project. You can also use Composer if the Laravel installer isn’t installed.
composer create-project laravel/laravel my-app
cd my-app
⚛️ Step 2: Install React + TypeScript
npm install npm install react react-dom @vitejs/plugin-react npm install --save-dev typescript @types/react @types/react-dom npx tsc --init
Installs React, TypeScript, and Vite's React plugin.
npx tsc --init
generates a tsconfig.json
to configure TypeScript.
🔧 Configure Vite
Update vite.config.js
:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel({
input: ['resources/js/app.tsx'],
refresh: true,
}),
react(),
],
});
This tells Vite to:
- Compile the React entry file (
app.tsx
) - Use React plugin for JSX support
- Enable hot reload (
refresh: true
)
🛠️ Update TypeScript Config
Update tsconfig.json
with:
{
"compilerOptions": {
"jsx": "react-jsx", // Required for JSX syntax
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Ensures proper TypeScript behavior with React and modern ES features.
📁 Step 3: Add React Entry Files
📄 resources/js/app.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/App';
const root = ReactDOM.createRoot(document.getElementById('app')!);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
This is your main entry file that mounts the React app into the DOM.
📄 resources/js/components/App.tsx
import React from 'react';
const App: React.FC = () => (
<div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
<h1>🚀 Laravel + React + TypeScript is Ready!</h1>
<p>You’re now running a fullstack Vite-powered app.</p>
</div>
);
export default App;
A basic React component to test rendering from Laravel.
🖼️ Step 4: Load React via Blade Template
📄 resources/views/app.blade.php
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My React App</title> @viteReactRefresh @vite('resources/js/app.tsx') </head> <body> <div id="app"></div> </body> </html>
This Blade view includes your compiled React app and enables HMR with Vite.
Update routes/web.php
:
Route::get('/', fn() => view('app'));
This route loads your Blade view and serves as the entry point for the React SPA.
🌐 Step 5: Setup a Simple API Route
Enable Laravel’s built-in API structure:
php artisan install:api
Scaffolds Laravel's default API middleware and file structure.
Then add a test route in routes/api.php
:
Route::get('/ping', function () { return response()->json([ 'message' => 'API is working ✅', 'time' => now(), ]); });
This route will be accessible at /api/ping
and confirms the API is working.
🧪 Step 6: Run Your App Locally
# Terminal 1: Laravel backend
php artisan serve
# Terminal 2: Vite frontend (React + HMR)
npm run dev
Now you can test:
- Frontend: http://127.0.0.1:8000
- API: http://127.0.0.1:8000/api/ping
🚀 Step 7: Production Preparation
🛠️ Build Assets
npm run build
Compiles and minifies your React app into public/build
.
🔐 Set Environment Variables
APP_ENV=production APP_DEBUG=false
Disable debug mode and tell Laravel to use cached config/assets.
⚡ Optimize Laravel
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan storage:link
Speeds up your Laravel app by caching configs, views, and routes.
📂 Final Project Structure
my-app/
├── routes/
│ ├── web.php # Loads React view
│ ├── api.php # API routes
├── resources/
│ ├── js/
│ │ ├── app.tsx
│ │ └── components/App.tsx
│ └── views/app.blade.php
├── public/
│ └── build/ # Production-ready assets from Vite
💡 Pro Tips
- Use React Router for SPA-style routing inside the dashboard.
- Add Axios to call your Laravel APIs from React.
- Use Sanctum for token/cookie-based authentication.
- Deploy with Laravel Forge, Docker, or any LAMP/VPS stack.
🧡 Built With
- Laravel 12.x
- React 18+
- TypeScript
- Vite
- ❤️ By Mustaf Abubakar
📫 me@mustafabubakar.com
🙌 Wrap Up
You've just built a scalable, modern fullstack application using Laravel and React — with TypeScript and Vite under the hood.
This architecture is great for:
- Admin panels
- Dashboards
- Hybrid web/mobile API apps
- Anything that needs Laravel + SPA
Thanks for reading! Feel free to connect, share feedback, or expand this starter into your next big thing.
Next up: In a follow-up post, we’ll add:
- User authentication with Sanctum
- React Router for navigation
- Protected routes and roles
- Deployment tips
📦 Get the Full Source Code
Everything we covered is available here:
🔗 GitHub – Mustafaa4A/laravel-react-typescript-starter
Feel free to star the repo or fork it as your own starter!