[For Intermediate-to-Advanced] Modern SPA Construction Guide with Laravel API + Vue.js
What You’ll Learn in This Article
- Basic setup of Laravel API resources and authentication tokens
- Implementing a simple Single Page Application with Vue 3 + Axios (or Inertia.js)
- Accessibility enhancements for screen readers, keyboard navigation, and more
- Practical sample code and key points for project structure
- Pre-deployment optimization & security checklist
Intended Audience
- Intermediate-to-advanced engineers who want to work seamlessly on both back end and front end
- Full-stack developers interested in integrating Laravel with Vue
- Project leads aiming to learn accessibility for modern SPAs
Accessibility Level: ★★★★★
Covers screen reader support, keyboard focus traps, aria-live for dynamic announcements, and more
Introduction: Why SPA + API Is Essential Today
In recent years, for both user experience (UX) and development efficiency,
combining a server-side API with a client-side JavaScript framework
to build a Single Page Application (SPA) has become mainstream.
Laravel offers a robust API foundation, and Vue.js provides a rich interface
that’s simple to implement♡
- Instant Updates: Partial re-renders for a smooth experience
- Parallel Development: Front end and back end evolve side by side
- Reusability: Expose the same API for internal tools or mobile apps
In this article, we’ll cover everything from initial setup to token auth,
component design, accessibility, and beyond.
1. Preparing the Laravel API: Resources & Token Authentication
1.1 Defining an API Resource
// app/Http/Controllers/Api/PostController.php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
return Post::orderBy('created_at', 'desc')->get();
}
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|string|max:100',
'body' => 'required|string',
]);
return Post::create($data);
}
}
- Register routes in
routes/api.php
so thatGET/POST api/posts
returns JSON. - You can further format responses using Eloquent API Resources.
1.2 Enabling Token Auth with Sanctum
composer require laravel/sanctum
php artisan vendor:publish --tag=sanctum-migrations
php artisan migrate
// app/Models/User.php
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
// …
}
// routes/api.php
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('posts', PostController::class);
});
- Issue a token with
$user->createToken('token-name')->plainTextToken
. - On the front end, send
Authorization: Bearer <token>
in the request headers.
2. Front End with Vue 3 + Axios
2.1 Example Project Structure
frontend/
├─ src/
│ ├─ components/
│ │ ├─ PostList.vue
│ │ └─ PostForm.vue
│ ├─ composables/
│ │ └─ useAuth.js
│ ├─ App.vue
│ └─ main.js
└─ package.json
- components/: Reusable UI parts, not page-specific
- composables/: Shared logic for auth tokens and API calls
2.2 Setting Up Axios
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import axios from 'axios';
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.withCredentials = true;
createApp(App).mount('#app');
// src/composables/useAuth.js
import axios from 'axios';
import { ref } from 'vue';
export function useAuth() {
const token = ref(localStorage.getItem('token') || '');
axios.interceptors.request.use(config => {
if (token.value) {
config.headers.Authorization = `Bearer ${token.value}`;
}
return config;
});
const login = async credentials => {
const res = await axios.post('/login', credentials);
token.value = res.data.token;
localStorage.setItem('token', token.value);
};
return { token, login };
}
- Use an interceptor to attach the Authorization header globally.
- Store the token in
localStorage
so it persists across page reloads.
3. Accessibility Enhancement Points
3.1 Announce Dynamic Updates with aria-live
<!-- PostList.vue -->
<template>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
<div aria-live="polite" class="sr-only">
{{ posts.length }} posts are now visible
</div>
</template>
aria-live="polite"
lets screen readers announce changes in post count.
3.2 Keyboard Focus Trap
<!-- Modal.vue (excerpt) -->
<div
role="dialog"
aria-modal="true"
tabindex="-1"
@keydown.tab.prevent="trapFocus"
ref="dialog"
>
<!-- Code to cycle focus among interactive elements -->
</div>
- Prevent Tab from escaping the modal.
- Always implement an Escape key handler to close it.
4. Pre-Deployment Optimization & Security Checklist
- API: Add rate limiting (Throttle) to prevent overload
- CORS: Restrict allowed origins to the minimum necessary
- Front End: Use code splitting (dynamic imports) to speed up initial load
- HTTPS: Enforce SSL/TLS
- Environment Variables: Manage tokens and keys in
.env
, not in source code
5. Summary & Next Steps
- Laravel API: Secure token authentication via Sanctum
- Vue 3 + Axios: Smart auth integration with interceptors
- Accessibility: Ensure usability with
aria-live
and keyboard traps - Optimization & Security: Harden your API and front end before launch
Use this article to master a modern SPA architecture♡
Combine with Inertia.js or Vue Router for an even richer, more intuitive user experience.