Next.js and Laravel create powerful full-stack architecture. Next.js handles frontend with SSR/SSG, Laravel provides robust API backend. At ZIRA Software, this stack powers enterprise applications with optimal performance.
Architecture
Next.js Frontend (Port 3000)
↓ API calls
Laravel API Backend (Port 8000)
↓
Database
Laravel API Setup
laravel new backend-api
cd backend-api
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
API routes:
// routes/api.php
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
Route::apiResource('posts', PostController::class);
});
CORS configuration:
// config/cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_origins' => ['http://localhost:3000'],
'supports_credentials' => true,
Next.js Setup
npx create-next-app@latest frontend
cd frontend
npm install axios swr
API client:
// lib/api.js
import axios from 'axios';
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
withCredentials: true,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
export default api;
Authentication
Login component:
// components/LoginForm.js
import { useState } from 'react';
import { useRouter } from 'next/router';
import api from '../lib/api';
export default function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const handleSubmit = async (e) => {
e.preventDefault();
try {
// Get CSRF cookie
await api.get('/sanctum/csrf-cookie');
// Login
await api.post('/api/login', { email, password });
router.push('/dashboard');
} catch (error) {
console.error('Login failed:', error);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e)=> setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e)=> setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Login</button>
</form>
);
}
Data Fetching with SWR
// hooks/usePosts.js
import useSWR from 'swr';
import api from '../lib/api';
const fetcher = (url) => api.get(url).then((res) => res.data);
export function usePosts() {
const { data, error, mutate } = useSWR('/api/posts', fetcher);
return {
posts: data?.data,
isLoading: !error && !data,
isError: error,
mutate,
};
}
// pages/posts.js
import { usePosts } from '../hooks/usePosts';
export default function PostsPage() {
const { posts, isLoading } = usePosts();
if (isLoading) return <div>Loading...</div>;
return (
<div>
{posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
Server-Side Rendering
// pages/posts/[id].js
import api from '../../lib/api';
export default function PostPage({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
export async function getServerSideProps({ params }) {
const { data } = await api.get(`/api/posts/${params.id}`);
return {
props: {
post: data.data,
},
};
}
Static Site Generation
// pages/blog/[slug].js
export async function getStaticPaths() {
const { data } = await api.get('/api/posts');
const paths = data.data.map((post) => ({
params: { slug: post.slug },
}));
return { paths, fallback: 'blocking' };
}
export async function getStaticProps({ params }) {
const { data } = await api.get(`/api/posts/${params.slug}`);
return {
props: { post: data.data },
revalidate: 60, // Revalidate every 60 seconds
};
}
Protected Routes
// components/ProtectedRoute.js
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import useSWR from 'swr';
import api from '../lib/api';
export default function ProtectedRoute({ children }) {
const router = useRouter();
const { data: user, error } = useSWR('/api/user', (url) =>
api.get(url).then((res) => res.data)
);
useEffect(() => {
if (error) {
router.push('/login');
}
}, [error, router]);
if (!user) return <div>Loading...</div>;
return children;
}
Deployment
Laravel (API):
# Deploy to server or Laravel Vapor
# Configure .env
APP_URL=https://api.yourdomain.com
SANCTUM_STATEFUL_DOMAINS=yourdomain.com
Next.js:
# Deploy to Vercel
npm run build
# Environment variables
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
Conclusion
Next.js + Laravel provides modern full-stack development with optimal performance. Laravel handles data and authentication, Next.js delivers fast user experience.
Building full-stack applications? Contact ZIRA Software for Next.js + Laravel development.